Skip to content
This repository has been archived by the owner on Aug 21, 2024. It is now read-only.

Commit

Permalink
chore: move compilation logic to FeatureContracts enum
Browse files Browse the repository at this point in the history
Signed-off-by: Dori Medini <[email protected]>
  • Loading branch information
dorimedini-starkware committed May 21, 2024
1 parent 4bdfda2 commit 72f5494
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 25 deletions.
3 changes: 2 additions & 1 deletion crates/blockifier/src/test_utils.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod cairo_compile;
pub mod contracts;
pub mod declare;
pub mod deploy_account;
Expand Down Expand Up @@ -49,7 +50,7 @@ pub const TEST_ERC20_CONTRACT_CLASS_HASH: &str = "0x1010";
pub const ERC20_CONTRACT_PATH: &str =
"./ERC20_without_some_syscalls/ERC20/erc20_contract_without_some_syscalls_compiled.json";

#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CairoVersion {
Cairo0,
Cairo1,
Expand Down
21 changes: 21 additions & 0 deletions crates/blockifier/src/test_utils/cairo_compile.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use std::process::Command;

/// Compiles a Cairo0 program using the deprecated compiler.
pub fn cairo0_compile(path: String, extra_arg: Option<String>, debug_info: bool) -> Vec<u8> {
let mut command = Command::new("starknet-compile-deprecated");
if let Some(extra_arg) = extra_arg {
command.arg(extra_arg);
}
if !debug_info {
command.args([&path, "--no_debug_info"]);
}
let compile_output = command.output().unwrap();
let stderr_output = String::from_utf8(compile_output.stderr).unwrap();
assert!(compile_output.status.success(), "{stderr_output}");
compile_output.stdout
}

/// Compiles a Cairo1 program using the compiler version set in the Cargo.toml.
pub fn cairo1_compile(_path: String) -> Vec<u8> {
todo!();
}
63 changes: 59 additions & 4 deletions crates/blockifier/src/test_utils/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use strum_macros::EnumIter;
use crate::abi::abi_utils::selector_from_name;
use crate::abi::constants::CONSTRUCTOR_ENTRY_POINT_NAME;
use crate::execution::contract_class::{ContractClass, ContractClassV0, ContractClassV1};
use crate::test_utils::cairo_compile::{cairo0_compile, cairo1_compile};
use crate::test_utils::{get_raw_contract_class, CairoVersion};

// This file contains featured contracts, used for tests. Use the function 'test_state' in
Expand Down Expand Up @@ -55,8 +56,10 @@ const SECURITY_TEST_CONTRACT_NAME: &str = "security_tests_contract";
const TEST_CONTRACT_NAME: &str = "test_contract";

// ERC20 contract is in a unique location.
const ERC20_BASE_NAME: &str = "ERC20";
const ERC20_CONTRACT_PATH: &str =
"./ERC20_without_some_syscalls/ERC20/erc20_contract_without_some_syscalls_compiled.json";
const ERC20_CONTRACT_SOURCE_PATH: &str = "./ERC20_without_some_syscalls/ERC20/ERC20.cairo";

/// Enum representing all feature contracts.
/// The contracts that are implemented in both Cairo versions include a version field.
Expand All @@ -73,7 +76,7 @@ pub enum FeatureContract {
}

impl FeatureContract {
fn cairo_version(&self) -> CairoVersion {
pub fn cairo_version(&self) -> CairoVersion {
match self {
Self::AccountWithLongValidate(version)
| Self::AccountWithoutValidations(version)
Expand Down Expand Up @@ -118,18 +121,40 @@ impl FeatureContract {
}
}

pub fn get_compiled_path(&self) -> String {
let cairo_version = self.cairo_version();
let contract_name = match self {
fn contract_base_name(&self) -> &str {
match self {
Self::AccountWithLongValidate(_) => ACCOUNT_LONG_VALIDATE_NAME,
Self::AccountWithoutValidations(_) => ACCOUNT_WITHOUT_VALIDATIONS_NAME,
Self::Empty(_) => EMPTY_CONTRACT_NAME,
Self::FaultyAccount(_) => FAULTY_ACCOUNT_NAME,
Self::LegacyTestContract => LEGACY_CONTRACT_NAME,
Self::SecurityTests => SECURITY_TEST_CONTRACT_NAME,
Self::TestContract(_) => TEST_CONTRACT_NAME,
Self::ERC20 => ERC20_BASE_NAME,
}
}

pub fn get_source_path(&self) -> String {
match self {
// Special case: ERC20 contract in a different location.
Self::ERC20 => ERC20_CONTRACT_SOURCE_PATH.into(),
_not_erc20 => format!(
"feature_contracts/cairo{}/{}.cairo",
match self.cairo_version() {
CairoVersion::Cairo0 => "0",
CairoVersion::Cairo1 => "1",
},
self.contract_base_name()
),
}
}

pub fn get_compiled_path(&self) -> String {
let cairo_version = self.cairo_version();
let contract_name = match self {
// ERC20 is a special case - not in the feature_contracts directory.
Self::ERC20 => return ERC20_CONTRACT_PATH.into(),
_not_erc20 => self.contract_base_name(),
};
format!(
"feature_contracts/cairo{}/compiled/{}{}.json",
Expand All @@ -145,6 +170,31 @@ impl FeatureContract {
)
}

/// Compiles the feature contract and returns the compiled contract as a byte vector.
/// Panics if the contract is ERC20, as ERC20 contract recompilation is not supported.
pub fn compile(&self) -> Vec<u8> {
if matches!(self, Self::ERC20) {
panic!("ERC20 contract recompilation not supported.");
}
match self.cairo_version() {
CairoVersion::Cairo0 => {
let extra_arg: Option<String> = match self {
// Account contracts require the account_contract flag.
FeatureContract::AccountWithLongValidate(_)
| FeatureContract::AccountWithoutValidations(_)
| FeatureContract::FaultyAccount(_) => Some("--account_contract".into()),
FeatureContract::SecurityTests => Some("--disable_hint_validation".into()),
FeatureContract::Empty(_)
| FeatureContract::TestContract(_)
| FeatureContract::LegacyTestContract => None,
FeatureContract::ERC20 => unreachable!(),
};
cairo0_compile(self.get_source_path(), extra_arg, false)
}
CairoVersion::Cairo1 => cairo1_compile(self.get_source_path()),
}
}

pub fn set_cairo_version(&mut self, version: CairoVersion) {
match self {
Self::AccountWithLongValidate(v)
Expand Down Expand Up @@ -255,4 +305,9 @@ impl FeatureContract {
}
})
}

pub fn all_feature_contracts() -> impl Iterator<Item = Self> {
// ERC20 is a special case - not in the feature_contracts directory.
Self::all_contracts().filter(|contract| !matches!(contract, Self::ERC20))
}
}
29 changes: 9 additions & 20 deletions crates/blockifier/tests/feature_contracts_compatibility_test.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::fs;
use std::process::Command;

use blockifier::test_utils::contracts::FeatureContract;
use blockifier::test_utils::CairoVersion;
Expand Down Expand Up @@ -41,20 +40,12 @@ const FIX_COMMAND: &str = "FIX_FEATURE_TEST=1 cargo test -- --ignored";
// 2. for each `X.cairo` file in `TEST_CONTRACTS` there exists an `X_compiled.json` file in
// `COMPILED_CONTRACTS_SUBDIR` which equals `starknet-compile-deprecated X.cairo --no_debug_info`.
fn verify_feature_contracts_compatibility(fix: bool, cairo_version: CairoVersion) {
for (path_str, file_name, existing_compiled_path) in verify_and_get_files(cairo_version) {
for contract in FeatureContract::all_feature_contracts()
.filter(|contract| contract.cairo_version() == cairo_version)
{
// Compare output of cairo-file on file with existing compiled file.
let mut command = Command::new("starknet-compile-deprecated");
command.args([&path_str, "--no_debug_info"]);
if file_name.starts_with("account") {
command.arg("--account_contract");
}
if file_name.starts_with("security") {
command.arg("--disable_hint_validation");
}
let compile_output = command.output().unwrap();
let stderr_output = String::from_utf8(compile_output.stderr).unwrap();
assert!(compile_output.status.success(), "{stderr_output}");
let expected_compiled_output = compile_output.stdout;
let expected_compiled_output = contract.compile();
let existing_compiled_path = contract.get_compiled_path();

if fix {
fs::write(&existing_compiled_path, &expected_compiled_output).unwrap();
Expand All @@ -64,9 +55,9 @@ fn verify_feature_contracts_compatibility(fix: bool, cairo_version: CairoVersion

if String::from_utf8(expected_compiled_output).unwrap() != existing_compiled_contents {
panic!(
"{path_str} does not compile to {existing_compiled_path}.\nRun `{FIX_COMMAND}` to \
fix the expected test according to locally installed \
`starknet-compile-deprecated`.\n"
"{} does not compile to {existing_compiled_path}.\nRun `{FIX_COMMAND}` to fix the \
expected test according to locally installed `starknet-compile-deprecated`.\n",
contract.get_source_path()
);
}
}
Expand Down Expand Up @@ -119,9 +110,7 @@ fn verify_and_get_files(cairo_version: CairoVersion) -> Vec<(String, String, Str

#[test]
fn verify_feature_contracts_match_enum() {
let mut compiled_paths_from_enum: Vec<String> = FeatureContract::all_contracts()
// ERC20 is a special case - not in the feature_contracts directory.
.filter(|contract| !matches!(contract, FeatureContract::ERC20))
let mut compiled_paths_from_enum: Vec<String> = FeatureContract::all_feature_contracts()
.map(|contract| contract.get_compiled_path())
.collect();
let mut compiled_paths_on_filesystem: Vec<String> = verify_and_get_files(CairoVersion::Cairo0)
Expand Down

0 comments on commit 72f5494

Please sign in to comment.