From 229f57847b1c5dd80e78c2c427adf364145c1de6 Mon Sep 17 00:00:00 2001 From: Arni Hod Date: Thu, 1 Aug 2024 14:16:20 +0300 Subject: [PATCH] feat: post compilation size limit validation --- config/mempool/default_config.json | 5 +++++ crates/gateway/src/compilation.rs | 23 ++++++++++++++++++++++ crates/gateway/src/compilation_test.rs | 11 +++++++++++ crates/gateway/src/config.rs | 27 ++++++++++++++++++-------- crates/gateway/src/errors.rs | 8 ++++++++ 5 files changed, 66 insertions(+), 8 deletions(-) diff --git a/config/mempool/default_config.json b/config/mempool/default_config.json index b45b272aebc..e2b094cec4d 100644 --- a/config/mempool/default_config.json +++ b/config/mempool/default_config.json @@ -14,6 +14,11 @@ "privacy": "Public", "value": 81920 }, + "gateway_config.compiler_config.max_raw_casm_class_size": { + "description": "Limitation of contract class object size.", + "privacy": "Public", + "value": 4089446 + }, "gateway_config.network_config.ip": { "description": "The gateway server ip.", "privacy": "Public", diff --git a/crates/gateway/src/compilation.rs b/crates/gateway/src/compilation.rs index 1475286b60a..b123838d325 100644 --- a/crates/gateway/src/compilation.rs +++ b/crates/gateway/src/compilation.rs @@ -37,6 +37,7 @@ impl GatewayCompiler { let casm_contract_class = self.compile(cairo_lang_contract_class)?; validate_compiled_class_hash(&casm_contract_class, &tx.compiled_class_hash)?; + self.validate_casm_class_size(&casm_contract_class)?; Ok(ClassInfo::new( &ContractClass::V1(ContractClassV1::try_from(casm_contract_class)?), @@ -58,6 +59,28 @@ impl GatewayCompiler { Ok(casm_contract_class) } + + // TODO(Arni): consider validating the size of other members of the Casm class. Cosider removing + // the validation of the raw class size. The validation should be linked to the way the class is + // saved in Papyrus etc. + /// Validates that the Casm class is within size limit. Specifically, this function validates + /// the size of the serialized class. + fn validate_casm_class_size( + &self, + casm_contract_class: &CasmContractClass, + ) -> Result<(), GatewayError> { + let contract_class_object_size = serde_json::to_string(&casm_contract_class) + .expect("Unexpected error serializing Casm contract class.") + .len(); + if contract_class_object_size > self.config.max_raw_casm_class_size { + return Err(GatewayError::CasmContractClassObjectSizeTooLarge { + contract_class_object_size, + max_contract_class_object_size: self.config.max_raw_casm_class_size, + }); + } + + Ok(()) + } } /// Validates that the compiled class hash of the compiled contract class matches the supplied diff --git a/crates/gateway/src/compilation_test.rs b/crates/gateway/src/compilation_test.rs index 324f4e5a80f..a062023f8d8 100644 --- a/crates/gateway/src/compilation_test.rs +++ b/crates/gateway/src/compilation_test.rs @@ -63,6 +63,17 @@ fn test_compile_contract_class_bytecode_size_validation(declare_tx: RpcDeclareTr ) } +// TODO(Arni): Redesign this test once the compiler is passed with dependancy injection. +#[rstest] +fn test_compile_contract_class_raw_class_size_validation(declare_tx: RpcDeclareTransaction) { + let gateway_compiler = GatewayCompiler { + config: GatewayCompilerConfig { max_raw_casm_class_size: 1, ..Default::default() }, + }; + + let result = gateway_compiler.process_declare_tx(&declare_tx); + assert_matches!(result.unwrap_err(), GatewayError::CasmContractClassObjectSizeTooLarge { .. }) +} + #[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 b60cd57786c..d8d53b40392 100644 --- a/crates/gateway/src/config.rs +++ b/crates/gateway/src/config.rs @@ -234,25 +234,36 @@ impl StatefulTransactionValidatorConfig { } } } - #[derive(Clone, Copy, Debug, Serialize, Deserialize, Validate, PartialEq)] pub struct GatewayCompilerConfig { pub max_casm_bytecode_size: usize, + pub max_raw_casm_class_size: usize, } impl Default for GatewayCompilerConfig { fn default() -> Self { - Self { max_casm_bytecode_size: MAX_BYTECODE_SIZE } + Self { + max_casm_bytecode_size: MAX_BYTECODE_SIZE, + max_raw_casm_class_size: MAX_RAW_CLASS_SIZE, + } } } impl SerializeConfig for GatewayCompilerConfig { fn dump(&self) -> BTreeMap { - BTreeMap::from_iter([ser_param( - "max_casm_bytecode_size", - &self.max_casm_bytecode_size, - "Limitation of contract bytecode size.", - ParamPrivacyInput::Public, - )]) + BTreeMap::from_iter([ + ser_param( + "max_casm_bytecode_size", + &self.max_casm_bytecode_size, + "Limitation of contract bytecode size.", + ParamPrivacyInput::Public, + ), + ser_param( + "max_raw_casm_class_size", + &self.max_raw_casm_class_size, + "Limitation of contract class object size.", + ParamPrivacyInput::Public, + ), + ]) } } diff --git a/crates/gateway/src/errors.rs b/crates/gateway/src/errors.rs index a4ffe6af031..0009d3155ae 100644 --- a/crates/gateway/src/errors.rs +++ b/crates/gateway/src/errors.rs @@ -39,6 +39,14 @@ use crate::compiler_version::{VersionId, VersionIdError}; /// Errors directed towards the end-user, as a result of gateway requests. #[derive(Debug, Error)] pub enum GatewayError { + #[error( + "Cannot declare Casm contract class with size of {contract_class_object_size}; max \ + allowed size: {max_contract_class_object_size}." + )] + CasmContractClassObjectSizeTooLarge { + contract_class_object_size: usize, + max_contract_class_object_size: usize, + }, #[error(transparent)] CompilationError(#[from] CompilationUtilError), #[error(