diff --git a/Cargo.lock b/Cargo.lock index d41ab6db91b..061872a97b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8975,6 +8975,7 @@ dependencies = [ "async-trait", "axum", "blockifier", + "cairo-lang-sierra-to-casm", "cairo-lang-starknet-classes", "cairo-vm", "enum-assoc", @@ -9114,11 +9115,14 @@ dependencies = [ "cairo-lang-starknet-classes", "cairo-lang-utils", "mempool_test_utils", + "papyrus_config", + "rstest", "serde", "serde_json", "starknet-types-core", "starknet_api", "thiserror", + "validator", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f075e2c8d26..8ac2324ac73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,7 @@ cairo-felt = "0.9.1" cairo-lang-casm = "2.7.0" cairo-lang-runner = "2.7.0" cairo-lang-sierra = "=2.7.0" +cairo-lang-sierra-to-casm = "2.7.0" cairo-lang-starknet-classes = "2.7.0" cairo-lang-utils = "2.7.0" cairo-vm = "=1.0.0-rc6" diff --git a/config/mempool/default_config.json b/config/mempool/default_config.json index e6cf5f0a753..6ff21a4d1be 100644 --- a/config/mempool/default_config.json +++ b/config/mempool/default_config.json @@ -9,6 +9,11 @@ "privacy": "Public", "value": true }, + "gateway_config.compiler_config.sierra_to_casm_compiler_config.max_bytecode_size": { + "description": "Limitation of contract bytecode size.", + "privacy": "Public", + "value": 81920 + }, "gateway_config.network_config.ip": { "description": "The gateway server ip.", "privacy": "Public", diff --git a/crates/blockifier/Cargo.toml b/crates/blockifier/Cargo.toml index 36e30a4f2d6..7536f0e449d 100644 --- a/crates/blockifier/Cargo.toml +++ b/crates/blockifier/Cargo.toml @@ -38,7 +38,7 @@ num-integer.workspace = true num-rational.workspace = true num-traits.workspace = true once_cell.workspace = true -papyrus_config = { path = "../papyrus_config", version = "0.4.0-rc.0" } +papyrus_config.workspace = true paste.workspace = true phf.workspace = true rand = { workspace = true, optional = true } diff --git a/crates/gateway/Cargo.toml b/crates/gateway/Cargo.toml index cb31687938b..fedf252781a 100644 --- a/crates/gateway/Cargo.toml +++ b/crates/gateway/Cargo.toml @@ -38,6 +38,7 @@ validator.workspace = true [dev-dependencies] assert_matches.workspace = true +cairo-lang-sierra-to-casm.workspace = true mockall.workspace = true mockito = "1.4.0" num-bigint.workspace = true diff --git a/crates/gateway/src/compilation.rs b/crates/gateway/src/compilation.rs index f425100edea..17a7b5abb7d 100644 --- a/crates/gateway/src/compilation.rs +++ b/crates/gateway/src/compilation.rs @@ -5,11 +5,10 @@ use cairo_lang_starknet_classes::casm_contract_class::CasmContractClass; use cairo_lang_starknet_classes::contract_class::ContractClass as CairoLangContractClass; use starknet_api::core::CompiledClassHash; use starknet_api::rpc_transaction::RpcDeclareTransaction; -use starknet_sierra_compile::compile::compile_sierra_to_casm; +use starknet_sierra_compile::compile::SierraToCasmCompiler; use starknet_sierra_compile::errors::CompilationUtilError; use starknet_sierra_compile::utils::into_contract_class_for_compilation; -use crate::config::GatewayCompilerConfig; use crate::errors::{GatewayError, GatewayResult}; #[cfg(test)] @@ -19,8 +18,7 @@ mod compilation_test; // TODO(Arni): Pass the compiler with dependancy injection. #[derive(Clone)] pub struct GatewayCompiler { - #[allow(dead_code)] - pub config: GatewayCompilerConfig, + pub sierra_to_casm_compiler: SierraToCasmCompiler, } impl GatewayCompiler { @@ -52,7 +50,7 @@ impl GatewayCompiler { cairo_lang_contract_class: CairoLangContractClass, ) -> Result { let catch_unwind_result = - panic::catch_unwind(|| compile_sierra_to_casm(cairo_lang_contract_class)); + panic::catch_unwind(|| self.sierra_to_casm_compiler.compile(cairo_lang_contract_class)); let casm_contract_class = catch_unwind_result.map_err(|_| CompilationUtilError::CompilationPanic)??; diff --git a/crates/gateway/src/compilation_test.rs b/crates/gateway/src/compilation_test.rs index 162e04f9f5e..2a591df3be3 100644 --- a/crates/gateway/src/compilation_test.rs +++ b/crates/gateway/src/compilation_test.rs @@ -1,6 +1,8 @@ use assert_matches::assert_matches; use blockifier::execution::contract_class::ContractClass; +use cairo_lang_sierra_to_casm::compiler::CompilationError; use cairo_lang_starknet_classes::allowed_libfuncs::AllowedLibfuncsError; +use cairo_lang_starknet_classes::casm_contract_class::StarknetSierraCompilationError; use mempool_test_utils::starknet_api_test_utils::declare_tx as rpc_declare_tx; use rstest::{fixture, rstest}; use starknet_api::core::CompiledClassHash; @@ -9,6 +11,8 @@ use starknet_api::rpc_transaction::{ RpcDeclareTransactionV3, RpcTransaction, }; +use starknet_sierra_compile::compile::SierraToCasmCompiler; +use starknet_sierra_compile::config::SierraToCasmCompilationConfig; use starknet_sierra_compile::errors::CompilationUtilError; use crate::compilation::GatewayCompiler; @@ -16,7 +20,7 @@ use crate::errors::GatewayError; #[fixture] fn gateway_compiler() -> GatewayCompiler { - GatewayCompiler { config: Default::default() } + GatewayCompiler { sierra_to_casm_compiler: SierraToCasmCompiler { config: Default::default() } } } #[fixture] @@ -46,6 +50,25 @@ fn test_compile_contract_class_compiled_class_hash_mismatch( ); } +// TODO(Arni): Redesign this test once the compiler is passed with dependancy injection. +#[rstest] +fn test_compile_contract_class_bytecode_size_validation(declare_tx_v3: RpcDeclareTransactionV3) { + let gateway_compiler = GatewayCompiler { + sierra_to_casm_compiler: SierraToCasmCompiler { + config: SierraToCasmCompilationConfig { max_bytecode_size: 1 }, + }, + }; + + let result = gateway_compiler.process_declare_tx(&RpcDeclareTransaction::V3(declare_tx_v3)); + assert_matches!( + result.unwrap_err(), + GatewayError::CompilationError(CompilationUtilError::StarknetSierraCompilationError( + StarknetSierraCompilationError::CompilationError(err) + )) + if matches!(err.as_ref(), CompilationError::CodeSizeLimitExceeded) + ) +} + #[rstest] fn test_compile_contract_class_bad_sierra( gateway_compiler: GatewayCompiler, diff --git a/crates/gateway/src/config.rs b/crates/gateway/src/config.rs index 07bf2149384..6e991ac97a0 100644 --- a/crates/gateway/src/config.rs +++ b/crates/gateway/src/config.rs @@ -6,11 +6,15 @@ use papyrus_config::dumping::{append_sub_config_name, ser_param, SerializeConfig use papyrus_config::{ParamPath, ParamPrivacyInput, SerializedParam}; use serde::{Deserialize, Serialize}; use starknet_api::core::Nonce; +use starknet_sierra_compile::config::SierraToCasmCompilationConfig; use starknet_types_core::felt::Felt; use validator::Validate; use crate::compiler_version::VersionId; +const MAX_BYTECODE_SIZE: usize = 81920; +const MAX_RAW_CLASS_SIZE: usize = 4089446; + #[derive(Clone, Debug, Default, Serialize, Deserialize, Validate, PartialEq)] pub struct GatewayConfig { pub network_config: GatewayNetworkConfig, @@ -88,8 +92,8 @@ impl Default for StatelessTransactionValidatorConfig { validate_non_zero_l2_gas_fee: false, max_calldata_length: 4000, max_signature_length: 4000, - max_bytecode_size: 81920, - max_raw_class_size: 4089446, + max_bytecode_size: MAX_BYTECODE_SIZE, + max_raw_class_size: MAX_RAW_CLASS_SIZE, min_sierra_version: VersionId { major: 1, minor: 1, patch: 0 }, max_sierra_version: VersionId { major: 1, minor: 5, patch: usize::MAX }, } @@ -233,10 +237,15 @@ impl StatefulTransactionValidatorConfig { } #[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, Validate, PartialEq)] -pub struct GatewayCompilerConfig {} +pub struct GatewayCompilerConfig { + pub sierra_to_casm_compiler_config: SierraToCasmCompilationConfig, +} impl SerializeConfig for GatewayCompilerConfig { fn dump(&self) -> BTreeMap { - BTreeMap::new() + append_sub_config_name( + self.sierra_to_casm_compiler_config.dump(), + "sierra_to_casm_compiler_config", + ) } } diff --git a/crates/gateway/src/gateway.rs b/crates/gateway/src/gateway.rs index a7e1af48995..326b1141ac8 100644 --- a/crates/gateway/src/gateway.rs +++ b/crates/gateway/src/gateway.rs @@ -11,6 +11,7 @@ use starknet_api::transaction::TransactionHash; use starknet_mempool_infra::component_runner::{ComponentStartError, ComponentStarter}; use starknet_mempool_types::communication::SharedMempoolClient; use starknet_mempool_types::mempool_types::{Account, AccountState, MempoolInput}; +use starknet_sierra_compile::compile::SierraToCasmCompiler; use tracing::{info, instrument}; use crate::compilation::GatewayCompiler; @@ -154,7 +155,11 @@ pub fn create_gateway( mempool_client: SharedMempoolClient, ) -> Gateway { let state_reader_factory = Arc::new(RpcStateReaderFactory { config: rpc_state_reader_config }); - let gateway_compiler = GatewayCompiler { config: config.compiler_config }; + let gateway_compiler = GatewayCompiler { + sierra_to_casm_compiler: SierraToCasmCompiler { + config: config.compiler_config.sierra_to_casm_compiler_config, + }, + }; Gateway::new(config, state_reader_factory, gateway_compiler, mempool_client) } diff --git a/crates/gateway/src/gateway_test.rs b/crates/gateway/src/gateway_test.rs index 8c3b5603da8..1f7554b4ccb 100644 --- a/crates/gateway/src/gateway_test.rs +++ b/crates/gateway/src/gateway_test.rs @@ -13,13 +13,11 @@ use starknet_api::rpc_transaction::RpcTransaction; use starknet_api::transaction::TransactionHash; use starknet_mempool_types::communication::MockMempoolClient; use starknet_mempool_types::mempool_types::{Account, AccountState, MempoolInput, ThinTransaction}; +use starknet_sierra_compile::compile::SierraToCasmCompiler; +use starknet_sierra_compile::config::SierraToCasmCompilationConfig; use crate::compilation::GatewayCompiler; -use crate::config::{ - GatewayCompilerConfig, - StatefulTransactionValidatorConfig, - StatelessTransactionValidatorConfig, -}; +use crate::config::{StatefulTransactionValidatorConfig, StatelessTransactionValidatorConfig}; use crate::gateway::{add_tx, AppState, SharedMempoolClient}; use crate::state_reader_test_utils::{local_test_state_reader_factory, TestStateReaderFactory}; use crate::stateful_transaction_validator::StatefulTransactionValidator; @@ -32,19 +30,16 @@ pub fn app_state( ) -> AppState { AppState { stateless_tx_validator: StatelessTransactionValidator { - config: StatelessTransactionValidatorConfig { - validate_non_zero_l1_gas_fee: true, - max_calldata_length: 10, - max_signature_length: 2, - max_bytecode_size: 10000, - max_raw_class_size: 1000000, - ..Default::default() - }, + config: StatelessTransactionValidatorConfig::default(), }, stateful_tx_validator: Arc::new(StatefulTransactionValidator { config: StatefulTransactionValidatorConfig::create_for_testing(), }), - gateway_compiler: GatewayCompiler { config: GatewayCompilerConfig {} }, + gateway_compiler: GatewayCompiler { + sierra_to_casm_compiler: SierraToCasmCompiler { + config: SierraToCasmCompilationConfig::default(), + }, + }, state_reader_factory: Arc::new(state_reader_factory), mempool_client, } diff --git a/crates/gateway/src/stateful_transaction_validator_test.rs b/crates/gateway/src/stateful_transaction_validator_test.rs index d510a6424e3..595b0ba2511 100644 --- a/crates/gateway/src/stateful_transaction_validator_test.rs +++ b/crates/gateway/src/stateful_transaction_validator_test.rs @@ -19,10 +19,11 @@ use starknet_api::core::{ContractAddress, Nonce}; use starknet_api::felt; use starknet_api::rpc_transaction::RpcTransaction; use starknet_api::transaction::TransactionHash; +use starknet_sierra_compile::compile::SierraToCasmCompiler; use starknet_types_core::felt::Felt; use crate::compilation::GatewayCompiler; -use crate::config::{GatewayCompilerConfig, StatefulTransactionValidatorConfig}; +use crate::config::StatefulTransactionValidatorConfig; use crate::errors::{StatefulTransactionValidatorError, StatefulTransactionValidatorResult}; use crate::state_reader::{MockStateReaderFactory, StateReaderFactory}; use crate::state_reader_test_utils::local_test_state_reader_factory; @@ -83,9 +84,11 @@ fn test_stateful_tx_validator( ) { let optional_class_info = match &external_tx { RpcTransaction::Declare(declare_tx) => Some( - GatewayCompiler { config: GatewayCompilerConfig {} } - .process_declare_tx(declare_tx) - .unwrap(), + GatewayCompiler { + sierra_to_casm_compiler: SierraToCasmCompiler { config: Default::default() }, + } + .process_declare_tx(declare_tx) + .unwrap(), ), _ => None, }; diff --git a/crates/gateway/src/stateless_transaction_validator.rs b/crates/gateway/src/stateless_transaction_validator.rs index 3a0b77676b0..a7a19c977e0 100644 --- a/crates/gateway/src/stateless_transaction_validator.rs +++ b/crates/gateway/src/stateless_transaction_validator.rs @@ -140,6 +140,8 @@ impl StatelessTransactionValidator { &self, contract_class: &starknet_api::rpc_transaction::ContractClass, ) -> StatelessTransactionValidatorResult<()> { + // This validation happens naturally when 'starknet-sierra-compile' is run. We still want to + // have this validation as early as possible as part of the stateless transaction validator. let bytecode_size = contract_class.sierra_program.len(); if bytecode_size > self.config.max_bytecode_size { return Err(StatelessTransactionValidatorError::BytecodeSizeTooLarge { diff --git a/crates/starknet_sierra_compile/Cargo.toml b/crates/starknet_sierra_compile/Cargo.toml index ce02f26f92f..6ff8e79d461 100644 --- a/crates/starknet_sierra_compile/Cargo.toml +++ b/crates/starknet_sierra_compile/Cargo.toml @@ -12,12 +12,15 @@ workspace = true cairo-lang-sierra.workspace = true cairo-lang-starknet-classes.workspace = true cairo-lang-utils.workspace = true +papyrus_config.workspace = true serde.workspace = true serde_json.workspace = true starknet-types-core.workspace = true starknet_api.workspace = true thiserror.workspace = true +validator.workspace = true [dev-dependencies] assert_matches.workspace = true mempool_test_utils.workspace = true +rstest.workspace = true diff --git a/crates/starknet_sierra_compile/src/compile.rs b/crates/starknet_sierra_compile/src/compile.rs index 23ac70ce395..dcc364a1ec5 100644 --- a/crates/starknet_sierra_compile/src/compile.rs +++ b/crates/starknet_sierra_compile/src/compile.rs @@ -2,32 +2,29 @@ use cairo_lang_starknet_classes::allowed_libfuncs::ListSelector; use cairo_lang_starknet_classes::casm_contract_class::CasmContractClass; use cairo_lang_starknet_classes::contract_class::ContractClass; +use crate::config::SierraToCasmCompilationConfig; use crate::errors::CompilationUtilError; #[cfg(test)] #[path = "compile_test.rs"] pub mod compile_test; -pub struct SierraToCasmCompilationArgs { - list_selector: ListSelector, - add_pythonic_hints: bool, - max_bytecode_size: usize, -} -/// This function may panic. -pub fn compile_sierra_to_casm( - contract_class: ContractClass, -) -> Result { - let compilation_args = SierraToCasmCompilationArgs { - list_selector: ListSelector::DefaultList, - add_pythonic_hints: true, - max_bytecode_size: 1000000, - }; +#[derive(Clone)] +pub struct SierraToCasmCompiler { + pub config: SierraToCasmCompilationConfig, +} - contract_class.validate_version_compatible(compilation_args.list_selector)?; +impl SierraToCasmCompiler { + pub fn compile( + &self, + contract_class: ContractClass, + ) -> Result { + contract_class.validate_version_compatible(ListSelector::DefaultList)?; - Ok(CasmContractClass::from_contract_class( - contract_class, - compilation_args.add_pythonic_hints, - compilation_args.max_bytecode_size, - )?) + Ok(CasmContractClass::from_contract_class( + contract_class, + true, + self.config.max_bytecode_size, + )?) + } } diff --git a/crates/starknet_sierra_compile/src/compile_test.rs b/crates/starknet_sierra_compile/src/compile_test.rs index 14e0b3d130b..bea5973a3e8 100644 --- a/crates/starknet_sierra_compile/src/compile_test.rs +++ b/crates/starknet_sierra_compile/src/compile_test.rs @@ -4,26 +4,33 @@ use std::path::Path; use assert_matches::assert_matches; use cairo_lang_starknet_classes::allowed_libfuncs::AllowedLibfuncsError; use mempool_test_utils::{get_absolute_path, FAULTY_ACCOUNT_CLASS_FILE, TEST_FILES_FOLDER}; +use rstest::{fixture, rstest}; -use crate::compile::{compile_sierra_to_casm, CompilationUtilError}; +use crate::compile::{CompilationUtilError, SierraToCasmCompiler}; +use crate::config::SierraToCasmCompilationConfig; use crate::test_utils::contract_class_from_file; -#[test] -fn test_compile_sierra_to_casm() { +#[fixture] +fn compiler() -> SierraToCasmCompiler { + SierraToCasmCompiler { config: SierraToCasmCompilationConfig::default() } +} + +#[rstest] +fn test_compile_sierra_to_casm(compiler: 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; let contract_class = contract_class_from_file(sierra_path); - let casm_contract = compile_sierra_to_casm(contract_class).unwrap(); + let casm_contract = compiler.compile(contract_class).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. -#[test] -fn test_negative_flow_compile_sierra_to_casm() { +#[rstest] +fn test_negative_flow_compile_sierra_to_casm(compiler: 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); @@ -31,7 +38,7 @@ fn test_negative_flow_compile_sierra_to_casm() { // 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); + let result = compiler.compile(contract_class); assert_matches!( result, Err(CompilationUtilError::AllowedLibfuncsError(AllowedLibfuncsError::SierraProgramError)) diff --git a/crates/starknet_sierra_compile/src/config.rs b/crates/starknet_sierra_compile/src/config.rs new file mode 100644 index 00000000000..77dd2d1f4f6 --- /dev/null +++ b/crates/starknet_sierra_compile/src/config.rs @@ -0,0 +1,28 @@ +use std::collections::BTreeMap; + +use papyrus_config::dumping::{ser_param, SerializeConfig}; +use papyrus_config::{ParamPath, ParamPrivacyInput, SerializedParam}; +use serde::{Deserialize, Serialize}; +use validator::Validate; + +#[derive(Clone, Copy, Debug, Serialize, Deserialize, Validate, PartialEq)] +pub struct SierraToCasmCompilationConfig { + pub max_bytecode_size: usize, +} + +impl Default for SierraToCasmCompilationConfig { + fn default() -> Self { + Self { max_bytecode_size: 81920 } + } +} + +impl SerializeConfig for SierraToCasmCompilationConfig { + fn dump(&self) -> BTreeMap { + BTreeMap::from_iter([ser_param( + "max_bytecode_size", + &self.max_bytecode_size, + "Limitation of contract bytecode size.", + ParamPrivacyInput::Public, + )]) + } +} diff --git a/crates/starknet_sierra_compile/src/lib.rs b/crates/starknet_sierra_compile/src/lib.rs index d949f064bd1..162e53db719 100644 --- a/crates/starknet_sierra_compile/src/lib.rs +++ b/crates/starknet_sierra_compile/src/lib.rs @@ -1,6 +1,7 @@ //! A lib for compiling Sierra into Casm. pub mod compile; +pub mod config; pub mod errors; pub mod utils;