def test_load_data_after_init(): code = """\ func main(): ret end """ program = compile_cairo(code, PRIME) runner = CairoRunner(program, layout='plain') runner.initialize_segments() runner.initialize_main_entrypoint() runner.initialize_vm({}) addr = runner.segments.add() runner.load_data(addr, [42]) assert runner.vm_memory[addr] == 42
def verify_secure_runner(runner: CairoRunner, verify_builtins=True): """ Verifies the complete run in runner is safe to relocate and run by another Cairo program. Checks that: * No access to pure addresses (The entire run must be relocatable). * All segment offsets are non negative. * All segment accesses are continuous. * No access to builtin segments beyond the allowed region. This region is defined by the start ptr (0), and the ptr returned in final stack. * No access to program segment beyond program data. Note: There is no need to check that builtin segment accesses are continuous. They will be accessed continuously in the builtin itself. """ builtin_segments = runner.get_builtin_segments_info( ) if verify_builtins else {} builtin_segment_names = { seg.index: name for name, seg in builtin_segments.items() } builtin_segment_sizes = { seg.index: seg.size for seg in builtin_segments.values() } segment_usage = defaultdict(set) for addr in runner.vm_memory: # Check pure addresses. if not isinstance(addr, RelocatableValue): raise SecurityError(f'Accessed address {addr} is not relocatable.') # Check non negative offset. if addr.offset < 0: raise SecurityError( f'Accessed address {addr} has negative offset.') # Check builtin segment out of bounds. if addr.segment_index in builtin_segment_sizes: if not addr.offset < builtin_segment_sizes[addr.segment_index]: raise SecurityError( 'Out of bounds access to builtin segment ' f'{builtin_segment_names[addr.segment_index]} at {addr}.') # Check out of bounds for program segment. if addr.segment_index == runner.program_base.segment_index: if not addr.offset < len(runner.program.data): raise SecurityError( f'Out of bounds access to program segment at {addr}.') # Check usage. segment_usage[addr.segment_index].add(addr.offset) # Check continuity. for segment_index, usage in segment_usage.items(): if len(usage) != max(usage) + 1: # Find the non continuity for error message. i = 0 while i in usage: i += 1 raise SecurityError( f'Non continuous segment {segment_index} at offset {i}.')
def test_select_input_builtins(builtin_selection_indicators): """ Tests the select_input_builtins Cairo function: calls the function with different builtins selection and checks that the function returns the expected builtin pointers. """ # Setup runner. cairo_file = os.path.join(os.path.dirname(__file__), 'select_input_builtins.cairo') runner = CairoRunner.from_file(cairo_file, DEFAULT_PRIME) runner.initialize_segments() output_base = runner.segments.add() hash_base = runner.segments.add() range_check_base = runner.segments.add() signature_base = runner.segments.add() # Setup function. builtins_encoding = { builtin: int.from_bytes(builtin.encode('ascii'), 'big') for builtin in ['output', 'pedersen', 'range_check', 'ecdsa'] } all_builtins = [output_base, hash_base, range_check_base, signature_base] selected_builtin_encodings = [ builtin_encoding for builtin_encoding, is_builtin_selected in zip( builtins_encoding.values(), builtin_selection_indicators) if is_builtin_selected ] selected_builtins = [ builtin for builtin, is_builtin_selected in zip( all_builtins, builtin_selection_indicators) if is_builtin_selected ] all_encodings = create_memory_struct(runner, builtins_encoding.values()) selected_encodings = create_memory_struct(runner, selected_builtin_encodings) all_ptrs = create_memory_struct(runner, all_builtins) n_builtins = len(selected_builtin_encodings) args = [all_encodings, all_ptrs, selected_encodings, n_builtins] end = runner.initialize_function_entrypoint('select_input_builtins', args) # Setup context. runner.initialize_vm(hint_locals={}) runner.run_until_pc(end) runner.end_run() # Check result. context = runner.vm.run_context # 'select_input_builtins' should return the pointers to the selected builtins. return_values_addr = context.ap - n_builtins assert [ context.memory[return_values_addr + i] for i in range(len(selected_builtins))] == \ selected_builtins
def test_builtin_list(): # This should work. program = compile_cairo( code=[('%builtins output pedersen range_check ecdsa\n', '')], prime=PRIME) CairoRunner(program, layout='small') # These should fail. program = compile_cairo(code=[('%builtins pedersen output\n', '')], prime=PRIME) with pytest.raises( AssertionError, match=r"Expected builtin list \['output', 'pedersen'\] does not match " r"\['pedersen', 'output'\]."): CairoRunner(program, layout='small') program = compile_cairo(code=[('%builtins pedersen foo\n', '')], prime=PRIME) with pytest.raises( AssertionError, match=r'Builtins {\'foo\'} are not present in layout "small"'): CairoRunner(program, layout='small')
def runner_and_output_runner(): PRIME = 2**251 + 17 * 2**192 + 1 code = """ func main(): ret end """ program = compile_cairo(code=[(code, '')], prime=PRIME, add_start=True) runner = CairoRunner(program=program, layout='plain', proof_mode=True) runner.initialize_segments() output_builtin_runner = runner.builtin_runners[ 'output'] = OutputBuiltinRunner(included=True) output_builtin_runner.initialize_segments(runner=runner) runner.initialize_main_entrypoint() runner.initialize_vm(hint_locals={}) return runner, output_builtin_runner, output_builtin_runner.base
def test_run_until_label(): runner = CairoRunner.from_file(CAIRO_FILE, PRIME, proof_mode=True) runner.initialize_segments() runner.initialize_main_entrypoint() runner.initialize_vm({}) # Test runs. assert runner.vm.run_context.pc - runner.program_base == 0 runner.run_until_label(3) assert runner.vm.run_context.pc - runner.program_base == 3 assert runner.vm.current_step == 3 runner.run_until_label('label1') assert runner.vm.run_context.pc - runner.program_base == 6 assert runner.vm.current_step == 6 with pytest.raises(VmException, match='End of program was not reached'): runner.run_until_label('label0', max_steps=100) assert runner.vm.run_context.pc - runner.program_base == 8 assert runner.vm.current_step == 106 runner.run_until_next_power_of_2() assert runner.vm.current_step == 128
def verify_secure_runner(runner: CairoRunner, verify_builtins=True): """ Verifies the complete run in runner is safe to relocate and run by another Cairo program. Checks that: * No access to pure addresses (The entire run must be relocatable). * All segment offsets are non negative. * No access to builtin segments beyond the allowed region. This region is defined by the start ptr (0), and the ptr returned in final stack. * No access to program segment beyond program data. Note: The continuity of builtin segments is checked in builtin specific checks. """ builtin_segments = runner.get_builtin_segments_info() if verify_builtins else {} builtin_segment_names = {seg.index: name for name, seg in builtin_segments.items()} builtin_segment_sizes = {seg.index: seg.size for seg in builtin_segments.values()} for addr in runner.vm_memory: # Check pure addresses. if not isinstance(addr, RelocatableValue): raise SecurityError(f'Accessed address {addr} is not relocatable.') # Check non negative offset. if addr.offset < 0: raise SecurityError(f'Accessed address {addr} has negative offset.') # Check builtin segment out of bounds. if addr.segment_index in builtin_segment_sizes: if not addr.offset < builtin_segment_sizes[addr.segment_index]: raise SecurityError( 'Out of bounds access to builtin segment ' f'{builtin_segment_names[addr.segment_index]} at {addr}.') # Check out of bounds for program segment. if addr.segment_index == runner.program_base.segment_index: if not addr.offset < len(runner.program.data): raise SecurityError(f'Out of bounds access to program segment at {addr}.') # Builtin specific checks. try: for builtin_runner in runner.builtin_runners.values(): builtin_runner.run_security_checks(runner) except Exception as exc: raise SecurityError(str(exc))
def test_validate_builtins(old_builtins, new_builtins, builtin_sizes, expect_throw): """ Tests the inner_validate_builtins_usage Cairo function: calls the function with different builtins usage and checks that the used builtins list was filled correctly. """ # Setup runner. runner = CairoRunner.from_file(CAIRO_FILE, DEFAULT_PRIME) assert len( runner.program.hints) == 0, 'Expecting validator to have no hints.' range_check_builtin = RangeCheckBuiltinRunner( included=True, ratio=None, inner_rc_bound=2**16, n_parts=small_instance.builtins['range_check'].n_parts) runner.builtin_runners['range_check_builtin'] = range_check_builtin runner.initialize_segments() # Setup function. old_builtins_ptr = create_memory_struct(runner, old_builtins) new_builtins_ptr = create_memory_struct(runner, new_builtins) builtins_sizes = create_memory_struct(runner, builtin_sizes) args = [ range_check_builtin.base, old_builtins_ptr, new_builtins_ptr, builtins_sizes, len(builtin_sizes), ] end = runner.initialize_function_entrypoint('validate_builtins', args) # Setup context. runner.initialize_vm(hint_locals={}) if expect_throw: with pytest.raises(VmException, match='is out of range'): runner.run_until_pc(end) else: runner.run_until_pc(end) runner.end_run()
def test_tracer_data(): code = """ %builtins output func main(output_ptr) -> (output_ptr): [ap] = 1000; ap++ let x = 2000 [ap] = x; ap++ let x = 5000 let y = [ap] [ap] = [ap - 2] + [ap - 1]; ap++ assert [output_ptr] = 1234 assert [output_ptr + 1] = 4321 ret end """ program: Program = compile_cairo(code=code, prime=PRIME, debug_info=True) runner = CairoRunner(program, layout='small') runner.initialize_segments() runner.initialize_main_entrypoint() runner.initialize_vm(hint_locals={}) runner.run_until_steps(steps=6) runner.end_run() runner.finalize_segments_by_effective_size() runner.relocate() memory = runner.relocated_memory trace = runner.relocated_trace tracer_data = TracerData(program=program, memory=memory, trace=trace, program_base=runner.relocate_value( runner.program_base)) # Test watch evaluator. watch_evaluator = WatchEvaluator(tracer_data=tracer_data, entry=tracer_data.trace[0]) with pytest.raises(TypeError, match='NoneType'): watch_evaluator.eval(None) assert watch_evaluator.eval_suppress_errors( 'x') == "FlowTrackingError: Invalid reference 'x'." watch_evaluator = WatchEvaluator(tracer_data=tracer_data, entry=tracer_data.trace[1]) assert watch_evaluator.eval('x') == '2000' watch_evaluator = WatchEvaluator(tracer_data=tracer_data, entry=tracer_data.trace[2]) assert watch_evaluator.eval('[ap]') == '3000' assert watch_evaluator.eval('[ap-1]') == '2000' assert watch_evaluator.eval('[ap-2]') == '1000' assert watch_evaluator.eval('[fp]') == '1000' assert watch_evaluator.eval('x') == '5000' # Test memory_accesses. assert memory[tracer_data.memory_accesses[0]['op1']] == 1000 assert memory[tracer_data.memory_accesses[1]['op1']] == 2000 assert tracer_data.memory_accesses[2]['dst'] == trace[2].ap assert tracer_data.memory_accesses[2]['op0'] == trace[2].ap - 2 assert tracer_data.memory_accesses[2]['op1'] == trace[2].ap - 1 # Test current identifier values. assert tracer_data.get_current_identifier_values(trace[0]) == { 'output_ptr': '21' } assert tracer_data.get_current_identifier_values(trace[1]) == { 'output_ptr': '21', 'x': '2000' } assert tracer_data.get_current_identifier_values(trace[2]) == { 'output_ptr': '21', 'x': '5000', 'y': '3000' } # '__temp1' identifier is already available in this step, but should not be returned as its # value is still unknown. assert tracer_data.get_current_identifier_values(trace[3]) == \ tracer_data.get_current_identifier_values(trace[4]) == { 'output_ptr': '21', 'x': '5000', 'y': '3000', '__temp0': '1234'} assert tracer_data.get_current_identifier_values(trace[5]) == { 'output_ptr': '21', 'x': '5000', 'y': '3000', '__temp0': '1234', '__temp1': '4321' }
def cairo_run(args): trace_file = args.trace_file if trace_file is None and args.tracer: # If --tracer is used, use a temporary file as trace_file. trace_file = tempfile.NamedTemporaryFile(mode='wb') memory_file = args.memory_file if memory_file is None and args.tracer: # If --tracer is used, use a temporary file as memory_file. memory_file = tempfile.NamedTemporaryFile(mode='wb') debug_info_file = args.debug_info_file if debug_info_file is None and args.tracer: # If --tracer is used, use a temporary file as debug_info_file. debug_info_file = tempfile.NamedTemporaryFile(mode='w') ret_code = 0 if args.program is not None: program: ProgramBase = Program.Schema().load(json.load(args.program)) initial_memory = MemoryDict() steps_input = args.steps else: raise NotImplementedError('--run_from_cairo_pie is not supported.') runner = CairoRunner(program=program, layout=args.layout, memory=initial_memory, proof_mode=args.proof_mode) runner.initialize_segments() end = runner.initialize_main_entrypoint() if args.run_from_cairo_pie is not None: # Add extra_segments. for segment_info in cairo_pie_input.metadata.extra_segments: runner.segments.add(size=segment_info.size) program_input = json.load(args.program_input) if args.program_input else {} runner.initialize_vm(hint_locals={'program_input': program_input}) try: if args.no_end: assert args.steps is not None, '--steps must specified when running with --no-end.' else: additional_steps = 1 if args.proof_mode else 0 max_steps = steps_input - additional_steps if steps_input is not None else None runner.run_until_pc(end, max_steps=max_steps) if args.proof_mode: # Run one more step to make sure the last pc that was executed (rather than the pc # after it) is __end__. runner.run_for_steps(1) runner.original_steps = runner.vm.current_step if args.min_steps: runner.run_until_steps(args.min_steps) if steps_input is not None: runner.run_until_steps(steps_input) elif args.proof_mode: runner.run_until_next_power_of_2() while not runner.check_used_cells(): runner.run_for_steps(1) runner.run_until_next_power_of_2() runner.end_run() except (VmException, AssertionError) as exc: if args.debug_error: print(f'Got an error:\n{exc}') ret_code = 1 else: raise exc if not args.no_end: runner.read_return_values() if args.no_end or not args.proof_mode: runner.finalize_segments_by_effective_size() else: # Finalize important segments by correct size. runner.finalize_segments() # Finalize all user segments by effective size. runner.finalize_segments_by_effective_size() if args.secure_run: verify_secure_runner(runner) if args.cairo_pie_output: runner.get_cairo_pie().to_file(args.cairo_pie_output) runner.relocate() if args.print_memory: runner.print_memory(relocated=args.relocate_prints) if args.print_output: runner.print_output() if args.print_info: runner.print_info(relocated=args.relocate_prints) # Skip builtin usage calculation if the execution stopped before reaching the end symbol. # Trying to calculate the builtin usage is likely to raise an exception and prevent the user # from opening the tracer. if args.proof_mode and not args.no_end: runner.print_builtin_usage() if trace_file is not None: field_bytes = math.ceil(program.prime.bit_length() / 8) write_binary_trace(trace_file, runner.relocated_trace) if memory_file is not None: field_bytes = math.ceil(program.prime.bit_length() / 8) write_binary_memory(memory_file, runner.relocated_memory, field_bytes) if args.air_public_input is not None: rc_min, rc_max = runner.get_perm_range_check_limits() write_air_public_input( layout=args.layout, public_input_file=args.air_public_input, memory=runner.relocated_memory, public_memory_addresses=runner.segments. get_public_memory_addresses(runner.segment_offsets), memory_segment_addresses=runner.get_memory_segment_addresses(), trace=runner.relocated_trace, rc_min=rc_min, rc_max=rc_max) if args.air_private_input is not None: assert args.trace_file is not None, \ '--trace_file must be set when --air_private_input is set.' assert args.memory_file is not None, \ '--memory_file must be set when --air_private_input is set.' json.dump( { 'trace_path': f'{os.path.abspath(trace_file.name)}', 'memory_path': f'{os.path.abspath(memory_file.name)}', **runner.get_air_private_input(), }, args.air_private_input, indent=4) print(file=args.air_private_input) args.air_private_input.flush() if debug_info_file is not None: json.dump(DebugInfo.Schema().dump(runner.get_relocated_debug_info()), debug_info_file) debug_info_file.flush() if args.tracer: CAIRO_TRACER = 'starkware.cairo.lang.tracer.tracer' subprocess.call( list( filter(None, [ sys.executable, '-m', CAIRO_TRACER, f'--program={args.program.name}', f'--trace={trace_file.name}', f'--memory={memory_file.name}', f'--air_public_input={args.air_public_input.name}' if args.air_public_input else None, f'--debug_info={debug_info_file.name}', ]))) return ret_code
def cairo_run(args, program_input): program: ProgramBase = load_program(args.program) initial_memory = MemoryDict() steps_input = args.steps runner = CairoRunner(program=program, layout=args.layout, memory=initial_memory, proof_mode=args.proof_mode) runner.initialize_segments() end = runner.initialize_main_entrypoint() runner.initialize_vm(hint_locals={'program_input': program_input}) try: additional_steps = 1 if args.proof_mode else 0 max_steps = steps_input - additional_steps if steps_input is not None else None runner.run_until_pc(end, max_steps=max_steps) runner.original_steps = runner.vm.current_step runner.end_run() except (VmException, AssertionError) as exc: raise exc runner.read_return_values() runner.finalize_segments_by_effective_size() verify_secure_runner(runner) runner.relocate() output = retrieveOutput(runner, args.exception) return output
def compile_and_run(code: str): """ Compiles the given code and runs it in the VM. """ program = compile_cairo(code, PRIME) runner = CairoRunner(program, layout='small', proof_mode=False) runner.initialize_segments() end = runner.initialize_main_entrypoint() runner.initialize_vm({}) runner.run_until_pc(end) runner.end_run()
def test_missing_exit_scope(): code = """\ func main(): %{ vm_enter_scope() %} ret end """ program = compile_cairo(code, PRIME) runner = CairoRunner(program, layout='small') runner.initialize_segments() end = runner.initialize_main_entrypoint() runner.initialize_vm({}) runner.run_until_pc(end) with pytest.raises( AssertionError, match=re.escape( 'Every enter_scope() requires a corresponding exit_scope().')): runner.end_run()
def test_bad_stop_ptr(): code = """\ %builtins output func main(output_ptr) -> (output_ptr): [ap] = 0; ap++ [ap - 1] = [output_ptr] [ap] = output_ptr + 3; ap++ # The correct return value is output_ptr + 1 ret end """ program = compile_cairo(code, PRIME) runner = CairoRunner(program, layout='small') runner.initialize_segments() end = runner.initialize_main_entrypoint() runner.initialize_vm({}) runner.run_until_pc(end) with pytest.raises( AssertionError, match='Invalid stop pointer for output. Expected: 2:1, found: 2:3' ): runner.read_return_values()
def run_code_in_runner(code, layout='plain'): program = compile_cairo(code, PRIME) runner = CairoRunner(program, layout=layout) runner.initialize_segments() end = runner.initialize_main_entrypoint() runner.initialize_vm(hint_locals={}) runner.run_until_pc(end) runner.end_run() runner.read_return_values() runner.finalize_segments_by_effective_size() return runner
def test_run_past_end(): code = """\ func main(): ret end """ program = compile_cairo(code, PRIME) runner = CairoRunner(program, layout='plain') runner.initialize_segments() runner.initialize_main_entrypoint() runner.initialize_vm({}) runner.run_for_steps(1) with pytest.raises( VmException, match='Error: Execution reached the end of the program.'): runner.run_for_steps(1)
def cairo_run(args): trace_needed = args.tracer or args.profile_output is not None trace_file = args.trace_file if trace_file is None and trace_needed: # If --tracer or --profile_output is used, use a temporary file as trace_file. trace_file = tempfile.NamedTemporaryFile(mode='wb') memory_file = args.memory_file if memory_file is None and trace_needed: # If --tracer or --profile_output is used, use a temporary file as memory_file. memory_file = tempfile.NamedTemporaryFile(mode='wb') debug_info_file = args.debug_info_file if debug_info_file is None and trace_needed: # If --tracer or --profile_output is used, use a temporary file as debug_info_file. debug_info_file = tempfile.NamedTemporaryFile(mode='w') ret_code = 0 cairo_pie_input = None if args.program is not None: program: ProgramBase = load_program(args.program) initial_memory = MemoryDict() steps_input = args.steps else: assert args.run_from_cairo_pie is not None assert args.steps is None and args.min_steps is None, \ '--steps and --min_steps cannot be specified in --run_from_cairo_pie mode.' cairo_pie_input = CairoPie.from_file(args.run_from_cairo_pie) try: cairo_pie_input.run_validity_checks() except Exception as exc: # Trim error message in case it's too long. msg = str(exc)[:10000] raise CairoRunError( f'Security check for the CairoPIE input failed: {msg}') program = cairo_pie_input.program initial_memory = cairo_pie_input.memory steps_input = cairo_pie_input.execution_resources.n_steps runner = CairoRunner(program=program, layout=args.layout, memory=initial_memory, proof_mode=args.proof_mode) runner.initialize_segments() end = runner.initialize_main_entrypoint() if args.run_from_cairo_pie is not None: # Add extra_segments. for segment_info in cairo_pie_input.metadata.extra_segments: runner.segments.add(size=segment_info.size) # Update the builtin runners' additional_data. for name, builtin_runner in runner.builtin_runners.items(): if name in cairo_pie_input.additional_data: builtin_runner.extend_additional_data( data=cairo_pie_input.additional_data[name], relocate_callback=lambda x: x, data_is_trusted=not args.secure_run) program_input = json.load(args.program_input) if args.program_input else {} runner.initialize_vm(hint_locals={'program_input': program_input}) try: if args.no_end: assert args.steps is not None, '--steps must be specified when running with --no-end.' else: additional_steps = 1 if args.proof_mode else 0 max_steps = steps_input - additional_steps if steps_input is not None else None runner.run_until_pc(end, max_steps=max_steps) if args.proof_mode: # Run one more step to make sure the last pc that was executed (rather than the pc # after it) is __end__. runner.run_for_steps(1) runner.original_steps = runner.vm.current_step if args.min_steps: runner.run_until_steps(args.min_steps) disable_trace_padding = False if steps_input is not None: runner.run_until_steps(steps_input) disable_trace_padding = True runner.end_run(disable_trace_padding=disable_trace_padding) except (VmException, AssertionError) as exc: if args.debug_error: print(f'Got an error:\n{exc}') ret_code = 1 else: raise exc if not args.no_end: runner.read_return_values() if not args.no_end and args.proof_mode: # Finalize important segments by correct size. runner.finalize_segments() if args.secure_run: verify_secure_runner(runner) if args.run_from_cairo_pie is not None: assert cairo_pie_input is not None assert cairo_pie_input == runner.get_cairo_pie(), \ 'The Cairo PIE input is not identical to the resulting Cairo PIE. ' \ 'This may indicate that the Cairo PIE was not generated by cairo_run.' if args.cairo_pie_output: runner.get_cairo_pie().to_file(args.cairo_pie_output) runner.relocate() if args.print_memory: runner.print_memory(relocated=args.relocate_prints) if args.print_output: runner.print_output() if args.print_info: runner.print_info(relocated=args.relocate_prints) # Skip builtin usage calculation if the execution stopped before reaching the end symbol. # Trying to calculate the builtin usage is likely to raise an exception and prevent the user # from opening the tracer. if args.proof_mode and not args.no_end: runner.print_builtin_usage() if trace_file is not None: field_bytes = math.ceil(program.prime.bit_length() / 8) write_binary_trace(trace_file, runner.relocated_trace) if memory_file is not None: field_bytes = math.ceil(program.prime.bit_length() / 8) write_binary_memory(memory_file, runner.relocated_memory, field_bytes) if args.air_public_input is not None: rc_min, rc_max = runner.get_perm_range_check_limits() write_air_public_input( layout=args.layout, public_input_file=args.air_public_input, memory=runner.relocated_memory, public_memory_addresses=runner.segments. get_public_memory_addresses(runner.segment_offsets), memory_segment_addresses=runner.get_memory_segment_addresses(), trace=runner.relocated_trace, rc_min=rc_min, rc_max=rc_max) if args.air_private_input is not None: assert args.trace_file is not None, \ '--trace_file must be set when --air_private_input is set.' assert args.memory_file is not None, \ '--memory_file must be set when --air_private_input is set.' json.dump( { 'trace_path': f'{os.path.abspath(trace_file.name)}', 'memory_path': f'{os.path.abspath(memory_file.name)}', **runner.get_air_private_input(), }, args.air_private_input, indent=4) print(file=args.air_private_input) args.air_private_input.flush() if debug_info_file is not None: json.dump(DebugInfo.Schema().dump(runner.get_relocated_debug_info()), debug_info_file) debug_info_file.flush() if args.tracer: CAIRO_TRACER = 'starkware.cairo.lang.tracer.tracer' subprocess.call( list( filter(None, [ sys.executable, '-m', CAIRO_TRACER, f'--program={args.program.name}', f'--trace={trace_file.name}', f'--memory={memory_file.name}', f'--air_public_input={args.air_public_input.name}' if args.air_public_input else None, f'--debug_info={debug_info_file.name}', ]))) if args.profile_output is not None: CAIRO_PROFILER = 'starkware.cairo.lang.tracer.profiler' subprocess.call( list( filter(None, [ sys.executable, '-m', CAIRO_PROFILER, f'--program={args.program.name}', f'--trace={trace_file.name}', f'--memory={memory_file.name}', f'--air_public_input={args.air_public_input.name}' if args.air_public_input else None, f'--debug_info={debug_info_file.name}', f'--profile_output={args.profile_output}', ]))) return ret_code