Sitelet https://github.com/Snezhkko/cairo/commit/2f0bf324f9ee39ec53119ad5730bc7dee5e87c74
Skip to content

Commit 2f0bf32

Browse files
authored
performance(sierra-to-casm): For contracts, prevented re-calling of felt252 deserialization. (starkware-libs#9444)
1 parent 20451fb commit 2f0bf32

8 files changed

Lines changed: 70 additions & 46 deletions

File tree

‎crates/bin/starknet-sierra-compile/src/main.rs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,11 @@ fn main() -> anyhow::Result<()> {
6969
entry_points_by_type,
7070
abi: None,
7171
};
72-
contract_class.validate_version_compatible(list_selector)?;
72+
let extracted = contract_class.extract_sierra_program(false)?;
73+
extracted.validate_version_compatible(list_selector)?;
7374
let casm_contract = CasmContractClass::from_contract_class(
7475
contract_class,
76+
extracted,
7577
args.add_pythonic_hints,
7678
args.max_bytecode_size,
7779
)

‎crates/bin/starknet-sierra-extract-code/src/main.rs‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ fn main() -> anyhow::Result<()> {
2727
)
2828
.with_context(|| "deserialization Failed.")?;
2929
let sierra_program = contract_class
30-
.extract_sierra_program()
31-
.with_context(|| "Failed parsing felt252s stream into Sierra program.")?;
30+
.extract_sierra_program(true)
31+
.with_context(|| "Failed parsing felt252s stream into Sierra program.")?
32+
.program;
3233
match args.output {
3334
Some(path) => fs::write(path, sierra_program.to_string())
3435
.with_context(|| "Failed to write Sierra program.")?,

‎crates/bin/starknet-sierra-upgrade-validate/src/main.rs‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,19 +345,26 @@ fn run_single(mut sierra_class: ContractClassInfo, config: &RunConfig) -> RunRes
345345
sierra_class.sierra_program[1].value = override_version.minor.into();
346346
sierra_class.sierra_program[2].value = override_version.patch.into();
347347
}
348+
let class_hash = sierra_class.class_hash.value;
348349
let contract_class = ContractClass {
349350
sierra_program: sierra_class.sierra_program,
350351
sierra_program_debug_info: None,
351352
contract_class_version: "0.1.0".to_string(),
352353
entry_points_by_type: sierra_class.entry_points_by_type,
353354
abi: None,
354355
};
355-
let class_hash = sierra_class.class_hash.value;
356-
if let Err(err) = contract_class.validate_version_compatible(config.list_selector.clone()) {
356+
let Ok(decoded_program) = contract_class.extract_sierra_program(false) else {
357+
return RunResult::ValidationFailure(ValidationFailure {
358+
class_hash,
359+
err: AllowedLibfuncsError::SierraProgramError,
360+
});
361+
};
362+
if let Err(err) = decoded_program.validate_version_compatible(config.list_selector.clone()) {
357363
return RunResult::ValidationFailure(ValidationFailure { class_hash, err });
358364
};
359365
let compiled_contract_class = match CasmContractClass::from_contract_class(
360366
contract_class,
367+
decoded_program,
361368
false,
362369
config.max_bytecode_size,
363370
) {

‎crates/cairo-lang-starknet-classes/src/casm_contract_class.rs‎

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,10 @@ use thiserror::Error;
4343

4444
use crate::allowed_libfuncs::AllowedLibfuncsError;
4545
use crate::compiler_version::{VersionId, current_compiler_version_id, current_sierra_version_id};
46-
use crate::contract_class::{ContractClass, ContractEntryPoint};
46+
use crate::contract_class::{ContractClass, ContractEntryPoint, ExtractedSierraProgram};
4747
use crate::contract_segmentation::{
4848
NestedIntList, SegmentationError, compute_bytecode_segment_lengths,
4949
};
50-
use crate::felt252_serde::{Felt252SerdeError, sierra_from_felt252s};
5150
use crate::keccak::starknet_keccak;
5251

5352
#[cfg(test)]
@@ -65,8 +64,6 @@ pub enum StarknetSierraCompilationError {
6564
#[error(transparent)]
6665
CompilationError(#[from] Box<CompilationError>),
6766
#[error(transparent)]
68-
Felt252SerdeError(#[from] Felt252SerdeError),
69-
#[error(transparent)]
7067
MetadataError(#[from] MetadataError),
7168
#[error(transparent)]
7269
AllowedLibfuncsError(#[from] AllowedLibfuncsError),
@@ -90,8 +87,6 @@ pub enum StarknetSierraCompilationError {
9087
DuplicateEntryPointSelector { selector: BigUint },
9188
#[error("Duplicate entry point function index {index}.")]
9289
DuplicateEntryPointSierraFunction { index: usize },
93-
#[error("Out of range value in serialization.")]
94-
ValueOutOfRange,
9590
#[error(
9691
"Cannot compile Sierra version {version_in_contract} with the current compiler (sierra \
9792
version: {version_of_compiler})"
@@ -335,11 +330,13 @@ impl TypeResolver<'_> {
335330
impl CasmContractClass {
336331
pub fn from_contract_class(
337332
contract_class: ContractClass,
333+
extracted_program: ExtractedSierraProgram,
338334
add_pythonic_hints: bool,
339335
max_bytecode_size: usize,
340336
) -> Result<Self, StarknetSierraCompilationError> {
341337
Ok(Self::from_contract_class_with_debug_info(
342338
contract_class,
339+
extracted_program,
343340
add_pythonic_hints,
344341
max_bytecode_size,
345342
)?
@@ -348,17 +345,12 @@ impl CasmContractClass {
348345

349346
pub fn from_contract_class_with_debug_info(
350347
contract_class: ContractClass,
348+
extracted_program: ExtractedSierraProgram,
351349
add_pythonic_hints: bool,
352350
max_bytecode_size: usize,
353351
) -> Result<(Self, CairoProgramDebugInfo), StarknetSierraCompilationError> {
354-
let prime = Felt252::prime();
355-
for felt252 in &contract_class.sierra_program {
356-
if felt252.value >= prime {
357-
return Err(StarknetSierraCompilationError::ValueOutOfRange);
358-
}
359-
}
360-
361-
let (sierra_version, _, program) = sierra_from_felt252s(&contract_class.sierra_program)?;
352+
let sierra_version = extracted_program.sierra_version;
353+
let program = extracted_program.program;
362354
let current_sierra_version = current_sierra_version_id();
363355
if !(sierra_version.major == current_sierra_version.major
364356
&& sierra_version.minor <= current_sierra_version.minor)
@@ -445,6 +437,7 @@ impl CasmContractClass {
445437
)?;
446438

447439
let AssembledCairoProgram { bytecode, hints } = cairo_program.assemble();
440+
let prime = Felt252::prime();
448441
let bytecode = bytecode
449442
.iter()
450443
.map(|big_int| {

‎crates/cairo-lang-starknet-classes/src/casm_contract_class_test.rs‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ use test_case::test_case;
1313
use crate::allowed_libfuncs::{
1414
BUILTIN_AUDITED_LIBFUNCS_LIST, ListSelector, lookup_allowed_libfuncs_list,
1515
};
16-
use crate::casm_contract_class::{BigUintAsHex, CasmContractClass, StarknetSierraCompilationError};
16+
use crate::casm_contract_class::{BigUintAsHex, CasmContractClass};
1717
use crate::contract_class::ContractClass;
18-
use crate::felt252_serde::sierra_from_felt252s;
18+
use crate::felt252_serde::{Felt252SerdeError, sierra_from_felt252s};
1919
use crate::test_utils::get_example_file_path;
2020

2121
#[test_case("test_contract__test_contract")]
@@ -25,11 +25,9 @@ fn test_casm_contract_from_contract_class_failure(name: &str) {
2525
std::fs::File::open(get_example_file_path(&format!("{name}.contract_class.json"))).unwrap();
2626
let mut contract_class: ContractClass = serde_json::from_reader(BufReader::new(f)).unwrap();
2727
contract_class.sierra_program[17] = BigUintAsHex { value: Felt252::prime() };
28-
29-
let add_pythonic_hints = false;
3028
assert_eq!(
31-
CasmContractClass::from_contract_class(contract_class, add_pythonic_hints, usize::MAX),
32-
Err(StarknetSierraCompilationError::ValueOutOfRange)
29+
contract_class.extract_sierra_program(false).err(),
30+
Some(Felt252SerdeError::InvalidInputForDeserialization)
3331
);
3432
}
3533

@@ -56,8 +54,10 @@ fn test_casm_contract_from_contract_class_from_contracts_crate(name: &str) {
5654
serde_json::from_reader(BufReader::new(std::fs::File::open(contract_path).unwrap()))
5755
.unwrap();
5856
let add_pythonic_hints = true;
57+
let program = contract.extract_sierra_program(false).unwrap();
5958
let casm_contract =
60-
CasmContractClass::from_contract_class(contract, add_pythonic_hints, usize::MAX).unwrap();
59+
CasmContractClass::from_contract_class(contract, program, add_pythonic_hints, usize::MAX)
60+
.unwrap();
6161
compare_contents_or_fix_with_path(
6262
&get_example_file_path(&format!("{name}.compiled_contract_class.json")),
6363
serde_json::to_string_pretty(&casm_contract).unwrap() + "\n",

‎crates/cairo-lang-starknet-classes/src/contract_class.rs‎

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
44
use num_bigint::BigUint;
55
use serde::{Deserialize, Serialize};
66
use serde_json::Value;
7+
use starknet_types_core::felt::Felt as Felt252;
78
use thiserror::Error;
89

910
use crate::abi::Contract;
@@ -58,14 +59,24 @@ impl ContractClass {
5859
})
5960
}
6061

61-
/// Extracts Sierra program from the ContractClass and populates it with debug info if
62-
/// available.
63-
pub fn extract_sierra_program(&self) -> Result<sierra::program::Program, Felt252SerdeError> {
64-
let (_, _, mut sierra_program) = sierra_from_felt252s(&self.sierra_program)?;
65-
if let Some(info) = &self.sierra_program_debug_info {
66-
info.populate(&mut sierra_program);
62+
/// Extracts Sierra program from the ContractClass into `ExtractedSierraProgram` and populates
63+
/// it with debug info if `populate_debug_info` is true, and the data is available.
64+
pub fn extract_sierra_program(
65+
&self,
66+
populate_debug_info: bool,
67+
) -> Result<ExtractedSierraProgram, Felt252SerdeError> {
68+
let prime = Felt252::prime();
69+
for felt252 in &self.sierra_program {
70+
if felt252.value >= prime {
71+
return Err(Felt252SerdeError::InvalidInputForDeserialization);
72+
}
73+
}
74+
let (sierra_version, compiler_version, mut program) =
75+
sierra_from_felt252s(&self.sierra_program)?;
76+
if populate_debug_info && let Some(info) = &self.sierra_program_debug_info {
77+
info.populate(&mut program);
6778
}
68-
Ok(sierra_program)
79+
Ok(ExtractedSierraProgram { program, sierra_version, compiler_version })
6980
}
7081

7182
/// Sanity checks the contract class.
@@ -79,26 +90,35 @@ impl ContractClass {
7990
);
8091
}
8192
}
93+
}
8294

95+
/// The Sierra program extracted from a contract class.
96+
pub struct ExtractedSierraProgram {
97+
/// The actual Sierra program.
98+
pub program: sierra::program::Program,
99+
/// The Sierra version used for the program.
100+
pub sierra_version: VersionId,
101+
/// The compiler version used for the program.
102+
pub compiler_version: VersionId,
103+
}
104+
impl ExtractedSierraProgram {
83105
/// Checks that all the used libfuncs in the contract class are allowed in the contract class
84106
/// Sierra version.
85107
pub fn validate_version_compatible(
86-
self: &ContractClass,
108+
&self,
87109
list_selector: ListSelector,
88110
) -> Result<(), AllowedLibfuncsError> {
89111
let list_name = list_selector.to_string();
90112
let allowed_libfuncs = lookup_allowed_libfuncs_list(list_selector)?;
91-
let (class_version, _, sierra_program) = sierra_from_felt252s(&self.sierra_program)
92-
.map_err(|_| AllowedLibfuncsError::SierraProgramError)?;
93-
for libfunc in &sierra_program.libfunc_declarations {
113+
for libfunc in &self.program.libfunc_declarations {
94114
match allowed_libfuncs.allowed_libfuncs.get(&libfunc.long_id.generic_id) {
95115
Some(None) => {}
96-
Some(Some(required_version)) if class_version.supports(*required_version) => {}
97-
Some(Some(required_version)) => {
116+
Some(Some(required)) if self.sierra_version.supports(*required) => {}
117+
Some(Some(required)) => {
98118
return Err(AllowedLibfuncsError::UnsupportedLibfuncAtVersion {
99119
invalid_libfunc: libfunc.long_id.generic_id.to_string(),
100-
required_version: *required_version,
101-
class_version,
120+
required_version: *required,
121+
class_version: self.sierra_version,
102122
});
103123
}
104124
None => {

‎crates/cairo-lang-starknet/src/compile.rs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,8 @@ pub fn starknet_compile(
250250
) -> Result<String> {
251251
let config = config.unwrap_or_default();
252252
let contract = compile_path(&crate_path, contract_path.as_deref(), config, inlining_strategy)?;
253-
contract.validate_version_compatible(allowed_libfuncs_list.unwrap_or_default())?;
253+
contract
254+
.extract_sierra_program(false)?
255+
.validate_version_compatible(allowed_libfuncs_list.unwrap_or_default())?;
254256
serde_json::to_string_pretty(&contract).with_context(|| "Serialization failed.")
255257
}

‎crates/cairo-lang-starknet/src/compile_test.rs‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,17 @@ fn test_compile_path_from_contracts_crate(example_contract_path: &str) {
3131
);
3232
let example_file_name = example_contract_path.replace("::", "__");
3333
let list_selector = ListSelector::ListName("all".to_string());
34-
contract.validate_version_compatible(list_selector).unwrap();
34+
let extracted = contract.extract_sierra_program(true).unwrap();
35+
extracted.validate_version_compatible(list_selector).unwrap();
3536

3637
compare_contents_or_fix_with_path(
3738
&get_example_file_path(format!("{example_file_name}.contract_class.json").as_str()),
3839
serde_json::to_string_pretty(&contract).unwrap() + "\n",
3940
);
4041

41-
let sierra_program = contract.extract_sierra_program().unwrap();
42-
4342
// There is a separate file for the sierra code as it is hard to review inside the json.
4443
compare_contents_or_fix_with_path(
4544
&get_example_file_path(format!("{example_file_name}.sierra").as_str()),
46-
sierra_program.to_string(),
45+
extracted.program.to_string(),
4746
);
4847
}

0 commit comments

Comments
 (0)