@@ -18,8 +18,8 @@ use cairo_lang_sierra::extensions::segment_arena::SegmentArenaType;
1818use cairo_lang_sierra:: extensions:: snapshot:: SnapshotType ;
1919use cairo_lang_sierra:: extensions:: starknet:: syscalls:: SystemType ;
2020use cairo_lang_sierra:: extensions:: structure:: StructType ;
21- use cairo_lang_sierra:: ids:: { ConcreteTypeId , GenericTypeId } ;
22- use cairo_lang_sierra:: program:: { ConcreteTypeLongId , GenericArg , TypeDeclaration } ;
21+ use cairo_lang_sierra:: ids:: { ConcreteTypeId , FunctionId , GenericTypeId } ;
22+ use cairo_lang_sierra:: program:: { ConcreteTypeLongId , GenericArg , StatementIdx , TypeDeclaration } ;
2323use cairo_lang_sierra_to_casm:: compiler:: {
2424 CairoProgramDebugInfo , CompilationError , SierraToCasmConfig ,
2525} ;
@@ -32,7 +32,7 @@ use cairo_lang_utils::require;
3232use cairo_lang_utils:: unordered_hash_map:: UnorderedHashMap ;
3333use cairo_lang_utils:: unordered_hash_set:: UnorderedHashSet ;
3434use convert_case:: { Case , Casing } ;
35- use itertools:: { Itertools , chain} ;
35+ use itertools:: { Itertools , chain, zip_eq } ;
3636use num_bigint:: BigUint ;
3737use num_integer:: Integer ;
3838use num_traits:: Signed ;
@@ -428,52 +428,6 @@ impl CasmContractClass {
428428 } ) ;
429429 }
430430 }
431- let entrypoint_ids = entrypoint_function_indices. map ( |idx| program. funcs [ idx] . id . clone ( ) ) ;
432- // TODO(lior): Remove this assert and condition once the equation solver is removed in major
433- // version 2.
434- assert_eq ! ( sierra_version. major, 1 ) ;
435- const NO_EQ_SOLVER_VERSION : VersionId = VersionId { major : 1 , minor : 4 , patch : 0 } ;
436- let no_eq_solver = sierra_version. supports ( NO_EQ_SOLVER_VERSION ) ;
437- let metadata_computation_config = MetadataComputationConfig {
438- function_set_costs : entrypoint_ids
439- . map ( |id| ( id, CostTokenMap :: from_iter ( [ ( CostTokenType :: Const , ENTRY_POINT_COST ) ] ) ) )
440- . collect ( ) ,
441- linear_gas_solver : no_eq_solver,
442- linear_ap_change_solver : no_eq_solver,
443- skip_non_linear_solver_comparisons : false ,
444- compute_runtime_costs : false ,
445- } ;
446- let program_info = ProgramRegistryInfo :: new ( & program) . map_err ( |err| {
447- StarknetSierraCompilationError :: CompilationError ( Box :: new (
448- CompilationError :: ProgramRegistryError ( err) ,
449- ) )
450- } ) ?;
451- let metadata = calc_metadata ( & program, & program_info, metadata_computation_config) ?;
452- let cairo_program = cairo_lang_sierra_to_casm:: compiler:: compile (
453- & program,
454- & program_info,
455- & metadata,
456- SierraToCasmConfig { gas_usage_check : true , max_bytecode_size } ,
457- ) ?;
458-
459- let AssembledCairoProgram { bytecode, hints } = cairo_program. assemble ( ) ;
460- let prime = Felt252 :: prime ( ) ;
461- let bytecode = bytecode
462- . iter ( )
463- . map ( |big_int| {
464- let ( _q, reminder) = big_int. magnitude ( ) . div_rem ( & prime) ;
465- BigUintAsHex {
466- value : if big_int. is_negative ( ) { & prime - reminder } else { reminder } ,
467- }
468- } )
469- . collect_vec ( ) ;
470-
471- const CONTRACT_SEGMENTATION_VERSION : VersionId = VersionId { major : 1 , minor : 5 , patch : 0 } ;
472- let bytecode_segment_lengths = if sierra_version. supports ( CONTRACT_SEGMENTATION_VERSION ) {
473- Some ( compute_bytecode_segment_lengths ( & program, & cairo_program, bytecode. len ( ) ) ?)
474- } else {
475- None
476- } ;
477431
478432 let builtin_types = UnorderedHashSet :: < GenericTypeId > :: from_iter ( [
479433 RangeCheckType :: id ( ) ,
@@ -489,11 +443,12 @@ impl CasmContractClass {
489443 MulModType :: id ( ) ,
490444 ] ) ;
491445
492- let as_casm_entry_point = |contract_entry_point : ContractEntryPoint | {
446+ // Validates the entry point's signature, returning its entry statement, function id and
447+ // builtin names.
448+ let validate_entry_point = |contract_entry_point : & ContractEntryPoint | {
493449 let Some ( function) = program. funcs . get ( contract_entry_point. function_idx ) else {
494450 return Err ( StarknetSierraCompilationError :: EntryPointError ) ;
495451 } ;
496- let statement_id = function. entry_point ;
497452
498453 // The expected return types are [builtins.., gas_builtin, system, PanicResult].
499454 let ( panic_result, output_builtins) = function
@@ -543,33 +498,92 @@ impl CasmContractClass {
543498 name => name. to_case ( Case :: Snake ) ,
544499 } )
545500 . collect_vec ( ) ;
501+ Ok ( ( function. entry_point , & function. id , builtins) )
502+ } ;
546503
547- let code_offset = cairo_program
548- . debug_info
549- . sierra_statement_info
550- . get ( statement_id. 0 )
551- . ok_or ( StarknetSierraCompilationError :: EntryPointError ) ?
552- . start_offset ;
553- assert_eq ! (
554- metadata. gas_info. function_costs[ & function. id] ,
555- CostTokenMap :: from_iter( [ ( CostTokenType :: Const , ENTRY_POINT_COST as i64 ) ] ) ,
556- "Unexpected entry point cost."
557- ) ;
558- Ok :: < CasmContractEntryPoint , StarknetSierraCompilationError > ( CasmContractEntryPoint {
559- selector : contract_entry_point. selector ,
560- offset : code_offset,
561- builtins,
562- } )
504+ // Validate all entry-point signatures before the (much heavier) compilation, keeping the
505+ // validated info for building the CASM entry points afterwards.
506+ let validate_entry_points = |entry_points : & [ ContractEntryPoint ] | {
507+ entry_points. iter ( ) . map ( validate_entry_point) . collect :: < Result < Vec < _ > , _ > > ( )
563508 } ;
509+ let external_infos = validate_entry_points ( & contract_class. entry_points_by_type . external ) ?;
510+ let l1_handler_infos =
511+ validate_entry_points ( & contract_class. entry_points_by_type . l1_handler ) ?;
512+ let constructor_infos =
513+ validate_entry_points ( & contract_class. entry_points_by_type . constructor ) ?;
564514
565- let as_casm_entry_points = |contract_entry_points : Vec < ContractEntryPoint > | {
566- let mut entry_points = vec ! [ ] ;
567- for contract_entry_point in contract_entry_points {
568- entry_points. push ( as_casm_entry_point ( contract_entry_point) ?) ;
569- }
570- Ok :: < Vec < CasmContractEntryPoint > , StarknetSierraCompilationError > ( entry_points)
515+ let entrypoint_ids = entrypoint_function_indices. map ( |idx| program. funcs [ idx] . id . clone ( ) ) ;
516+ // TODO(lior): Remove this assert and condition once the equation solver is removed in major
517+ // version 2.
518+ assert_eq ! ( sierra_version. major, 1 ) ;
519+ const NO_EQ_SOLVER_VERSION : VersionId = VersionId { major : 1 , minor : 4 , patch : 0 } ;
520+ let no_eq_solver = sierra_version. supports ( NO_EQ_SOLVER_VERSION ) ;
521+ let metadata_computation_config = MetadataComputationConfig {
522+ function_set_costs : entrypoint_ids
523+ . map ( |id| ( id, CostTokenMap :: from_iter ( [ ( CostTokenType :: Const , ENTRY_POINT_COST ) ] ) ) )
524+ . collect ( ) ,
525+ linear_gas_solver : no_eq_solver,
526+ linear_ap_change_solver : no_eq_solver,
527+ skip_non_linear_solver_comparisons : false ,
528+ compute_runtime_costs : false ,
529+ } ;
530+ let program_info = ProgramRegistryInfo :: new ( & program) . map_err ( |err| {
531+ StarknetSierraCompilationError :: CompilationError ( Box :: new (
532+ CompilationError :: ProgramRegistryError ( err) ,
533+ ) )
534+ } ) ?;
535+ let metadata = calc_metadata ( & program, & program_info, metadata_computation_config) ?;
536+ let cairo_program = cairo_lang_sierra_to_casm:: compiler:: compile (
537+ & program,
538+ & program_info,
539+ & metadata,
540+ SierraToCasmConfig { gas_usage_check : true , max_bytecode_size } ,
541+ ) ?;
542+
543+ let AssembledCairoProgram { bytecode, hints } = cairo_program. assemble ( ) ;
544+ let prime = Felt252 :: prime ( ) ;
545+ let bytecode = bytecode
546+ . iter ( )
547+ . map ( |big_int| {
548+ let ( _q, reminder) = big_int. magnitude ( ) . div_rem ( & prime) ;
549+ BigUintAsHex {
550+ value : if big_int. is_negative ( ) { & prime - reminder } else { reminder } ,
551+ }
552+ } )
553+ . collect_vec ( ) ;
554+
555+ const CONTRACT_SEGMENTATION_VERSION : VersionId = VersionId { major : 1 , minor : 5 , patch : 0 } ;
556+ let bytecode_segment_lengths = if sierra_version. supports ( CONTRACT_SEGMENTATION_VERSION ) {
557+ Some ( compute_bytecode_segment_lengths ( & program, & cairo_program, bytecode. len ( ) ) ?)
558+ } else {
559+ None
571560 } ;
572561
562+ let as_casm_entry_points =
563+ |contract_entry_points : Vec < ContractEntryPoint > ,
564+ infos : Vec < ( StatementIdx , & FunctionId , Vec < String > ) > | {
565+ zip_eq ( contract_entry_points, infos)
566+ . map ( |( contract_entry_point, ( statement_id, function_id, builtins) ) | {
567+ let code_offset = cairo_program. debug_info . sierra_statement_info
568+ [ statement_id. 0 ]
569+ . start_offset ;
570+ assert_eq ! (
571+ metadata. gas_info. function_costs[ function_id] ,
572+ CostTokenMap :: from_iter( [ (
573+ CostTokenType :: Const ,
574+ ENTRY_POINT_COST as i64
575+ ) ] ) ,
576+ "Unexpected entry point cost."
577+ ) ;
578+ CasmContractEntryPoint {
579+ selector : contract_entry_point. selector ,
580+ offset : code_offset,
581+ builtins,
582+ }
583+ } )
584+ . collect_vec ( )
585+ } ;
586+
573587 let pythonic_hints = if add_pythonic_hints {
574588 Some (
575589 hints
@@ -592,9 +606,18 @@ impl CasmContractClass {
592606 hints,
593607 pythonic_hints,
594608 entry_points_by_type : CasmContractEntryPoints {
595- external : as_casm_entry_points ( contract_class. entry_points_by_type . external ) ?,
596- l1_handler : as_casm_entry_points ( contract_class. entry_points_by_type . l1_handler ) ?,
597- constructor : as_casm_entry_points ( contract_class. entry_points_by_type . constructor ) ?,
609+ external : as_casm_entry_points (
610+ contract_class. entry_points_by_type . external ,
611+ external_infos,
612+ ) ,
613+ l1_handler : as_casm_entry_points (
614+ contract_class. entry_points_by_type . l1_handler ,
615+ l1_handler_infos,
616+ ) ,
617+ constructor : as_casm_entry_points (
618+ contract_class. entry_points_by_type . constructor ,
619+ constructor_infos,
620+ ) ,
598621 } ,
599622 } ;
600623
0 commit comments