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 16, 2024
1 parent f39ef95 commit 535a9fc
Show file tree
Hide file tree
Showing 8 changed files with 1,416 additions and 1 deletion.
13 changes: 13 additions & 0 deletions Cargo.lock

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

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[workspace]
resolver = "2"
members = ["crates/gateway", "crates/mempool", "crates/mempool_types", "crates/mempool_node", "crates/mempool_infra"]
members = ["crates/gateway", "crates/mempool", "crates/mempool_types", "crates/mempool_node", "crates/mempool_infra", "crates/starknet_sierra_compile"]

[workspace.package]
version = "0.0.0"
Expand All @@ -24,7 +24,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
18 changes: 18 additions & 0 deletions crates/starknet_sierra_compile/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[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
63 changes: 63 additions & 0 deletions crates/starknet_sierra_compile/src/compile.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
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 SierraToCasmCompliationArgs {
allowed_libfuncs_list_name: Option<String>,
allowed_libfuncs_list_file: Option<String>,
add_pythonic_hints: bool,
max_bytecode_size: usize,
}

#[derive(Debug, Error)]
pub enum CompilationUtilError {
#[error(transparent)]
AllowedLibfuncsError(#[from] AllowedLibfuncsError),
#[error("Both allowed libfuncs list name and file were supplied.")]
AllowedLibfuncsListSource,
#[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 = SierraToCasmCompliationArgs {
allowed_libfuncs_list_name: None,
allowed_libfuncs_list_file: None,
add_pythonic_hints: true,
max_bytecode_size: 1000000,
};

// TODO(task_executor).
tokio::task::spawn_blocking(move || starknet_sierra_compile(compilation_args, contract_class))
.await?
}

fn starknet_sierra_compile(
compilation_args: SierraToCasmCompliationArgs,
contract_class: ContractClass,
) -> Result<CasmContractClass, CompilationUtilError> {
let list_selector = ListSelector::new(
compilation_args.allowed_libfuncs_list_name,
compilation_args.allowed_libfuncs_list_file,
)
.ok_or(CompilationUtilError::AllowedLibfuncsListSource)?;
contract_class.validate_version_compatible(list_selector)?;

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

use cairo_lang_starknet_classes::allowed_libfuncs::AllowedLibfuncsError;
use cairo_lang_starknet_classes::contract_class::ContractClass;

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 contract_class = contract_class_from_file(sierra_path);
let ContractClass {
sierra_program,
sierra_program_debug_info,
contract_class_version,
entry_points_by_type,
abi,
} = contract_class;

let faulty_sierra_program = sierra_program[..100].to_vec();
let faulty_contract_class = ContractClass {
sierra_program: faulty_sierra_program,
sierra_program_debug_info,
contract_class_version,
entry_points_by_type,
abi,
};
let result = compile_sierra_to_casm(faulty_contract_class).await;
if let CompilationUtilError::AllowedLibfuncsError(AllowedLibfuncsError::SierraProgramError) =
result.unwrap_err()
{
return;
} else {
panic!("Unexpected error.")
}
}
4 changes: 4 additions & 0 deletions crates/starknet_sierra_compile/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod compile;

#[cfg(any(feature = "testing", test))]
pub mod test_utils;
37 changes: 37 additions & 0 deletions crates/starknet_sierra_compile/src/test_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
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;

/// See: https://github.com/starkware-libs/cairo/blob/d0c0f175a8855242d8c6265c55d3f97f8dfdce40/crates/bin/starknet-sierra-compile/src/main.rs#L34-L43
/// Same as `ContractClass` - but ignores `abi` in deserialization.
/// Enables loading old contract classes.
#[derive(Deserialize)]
struct ContractClassIgnoreAbi {
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 _abi: Option<serde_json::Value>,
}

pub(crate) fn contract_class_from_file(file: &Path) -> ContractClass {
let ContractClassIgnoreAbi {
sierra_program,
sierra_program_debug_info,
contract_class_version,
entry_points_by_type,
_abi,
} = 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 535a9fc

Please sign in to comment.