diff --git a/crates/starknet_api/Cargo.toml b/crates/starknet_api/Cargo.toml index b0697d0f18..8b2ece01c6 100644 --- a/crates/starknet_api/Cargo.toml +++ b/crates/starknet_api/Cargo.toml @@ -13,6 +13,7 @@ testing = [] bitvec.workspace = true cairo-lang-starknet-classes.workspace = true cairo-lang-runner.workspace = true +cairo-vm.workspace = true derive_more.workspace = true hex.workspace = true indexmap = { workspace = true, features = ["serde"] } diff --git a/crates/starknet_api/src/contract_class.rs b/crates/starknet_api/src/contract_class.rs index e633e3e4ae..d726959020 100644 --- a/crates/starknet_api/src/contract_class.rs +++ b/crates/starknet_api/src/contract_class.rs @@ -1,8 +1,13 @@ use cairo_lang_starknet_classes::casm_contract_class::CasmContractClass; +use cairo_vm::serde::deserialize_program::deserialize_array_of_bigint_hex; use serde::{Deserialize, Serialize}; use crate::core::CompiledClassHash; use crate::deprecated_contract_class::ContractClass as DeprecatedContractClass; +use crate::StarknetApiError; + +// Calldata. +pub const WORD_WIDTH: usize = 32; #[derive( Debug, Default, Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize, PartialOrd, Ord, @@ -47,3 +52,55 @@ pub struct ClassInfo { pub sierra_program_length: usize, pub abi_length: usize, } + +impl ClassInfo { + pub fn bytecode_length(&self) -> usize { + match &self.contract_class { + ContractClass::V0(contract_address) => { + let data = deserialize_array_of_bigint_hex(&contract_address.program.data) + .expect("Failed deserializing the program data."); + data.len() + } + ContractClass::V1(contract_address) => contract_address.bytecode.len(), + } + } + + pub fn contract_class(&self) -> ContractClass { + self.contract_class.clone() + } + + pub fn sierra_program_length(&self) -> usize { + self.sierra_program_length + } + + pub fn abi_length(&self) -> usize { + self.abi_length + } + + pub fn code_size(&self) -> usize { + (self.bytecode_length() + self.sierra_program_length()) + // We assume each felt is a word. + * WORD_WIDTH + + self.abi_length() + } + + pub fn new( + contract_class: &ContractClass, + sierra_program_length: usize, + abi_length: usize, + ) -> Result { + let (contract_class_version, condition) = match contract_class { + ContractClass::V0(_) => (0, sierra_program_length == 0), + ContractClass::V1(_) => (1, sierra_program_length > 0), + }; + + if condition { + Ok(Self { contract_class: contract_class.clone(), sierra_program_length, abi_length }) + } else { + Err(StarknetApiError::ContractClassVersionSierraProgramLengthMismatch { + contract_class_version, + sierra_program_length, + }) + } + } +} diff --git a/crates/starknet_api/src/lib.rs b/crates/starknet_api/src/lib.rs index 98f9f42eda..4ba0366232 100644 --- a/crates/starknet_api/src/lib.rs +++ b/crates/starknet_api/src/lib.rs @@ -49,6 +49,14 @@ pub enum StarknetApiError { InvalidStarknetVersion(Vec), #[error("NonzeroGasPrice cannot be zero.")] ZeroGasPrice, + #[error( + "Sierra program length must be > 0 for Cairo1, and == 0 for Cairo0. Got: \ + {sierra_program_length:?} for contract class version {contract_class_version:?}" + )] + ContractClassVersionSierraProgramLengthMismatch { + contract_class_version: u8, + sierra_program_length: usize, + }, } pub type StarknetApiResult = Result;