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

chore: move compilation logic to FeatureContracts enum #1869

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading