def test_program_start_property(): identifiers = IdentifierManager.from_dict({ ScopedName.from_string('some.main.__start__'): LabelDefinition(3), }) reference_manager = ReferenceManager() main_scope = ScopedName.from_string('some.main') # The label __start__ is in identifiers. program = Program(prime=0, data=[], hints={}, builtins=[], main_scope=main_scope, identifiers=identifiers, reference_manager=reference_manager) assert program.start == 3 # The label __start__ is not in identifiers. program = Program(prime=0, data=[], hints={}, builtins=[], main_scope=main_scope, identifiers=IdentifierManager(), reference_manager=reference_manager) assert program.start == 0
def from_files(cls, program_path: str, memory_path: str, trace_path: str, air_public_input: Optional[str], debug_info_path: Optional[str] = None): """ Factory method constructing TracerData from files. """ program = Program.Schema().load(json.load(open(program_path))) field_bytes = math.ceil(program.prime.bit_length() / 8) memory = read_memory(memory_path, field_bytes) trace = read_trace(trace_path) # Read AIR public input, if available and extract public memory addresses. if air_public_input is not None: public_input = PublicInput.Schema().load( json.load(open(air_public_input))) else: public_input = None debug_info = DebugInfo.Schema().load(json.load(open(debug_info_path))) \ if debug_info_path is not None else None # Construct the instance. return cls(program=program, memory=memory, trace=trace, air_public_input=public_input, debug_info=debug_info)
def load_program(program) -> ProgramBase: try: program_json = json.load(program) except json.JSONDecodeError as err: raise CairoRunError( 'Failed to load compiled program (not a valid JSON file). ' 'Did you compile the code before running it? ' f"Error: '{err}'") return Program.Schema().load(program_json)
def assemble(preprocessed_program: PreprocessedProgram, main_scope: ScopedName = ScopedName(), add_debug_info: bool = False, file_contents_for_debug_info: Dict[str, str] = {}) -> Program: data: List[int] = [] hints: Dict[int, List[CairoHint]] = {} debug_info = DebugInfo(instruction_locations={}, file_contents=file_contents_for_debug_info) \ if add_debug_info else None for inst in preprocessed_program.instructions: for hint, hint_flow_tracking_data in inst.hints: hints.setdefault(len(data), []).append( CairoHint(code=hint.hint_code, accessible_scopes=inst.accessible_scopes, flow_tracking_data=hint_flow_tracking_data)) if debug_info is not None and inst.instruction.location is not None: hint_locations: List[Optional[HintLocation]] = [] for hint, _ in inst.hints: if hint.location is None: hint_locations.append(None) else: hint_locations.append( HintLocation( location=hint.location, n_prefix_newlines=hint.hint.n_prefix_newlines, )) debug_info.instruction_locations[len(data)] = \ InstructionLocation( inst=inst.instruction.location, hints=hint_locations, accessible_scopes=inst.accessible_scopes, flow_tracking_data=inst.flow_tracking_data) data += [ word for word in encode_instruction(build_instruction(inst.instruction), prime=preprocessed_program.prime) ] if debug_info is not None: debug_info.add_autogen_file_contents() return Program(prime=preprocessed_program.prime, data=data, hints=hints, main_scope=main_scope, identifiers=preprocessed_program.identifiers, builtins=preprocessed_program.builtins, reference_manager=preprocessed_program.reference_manager, debug_info=debug_info)
def main(): parser = argparse.ArgumentParser( description='A tool to compute the hash of a cairo program') parser.add_argument('--program', type=argparse.FileType('r'), required=True, help='The name of the program json file.') parser.add_argument('--flavor', type=str, default='Release', choices=['Debug', 'Release', 'RelWithDebInfo'], help='Build flavor') args = parser.parse_args() with get_crypto_lib_context_manager(args.flavor): program = Program.Schema().load(json.load(args.program)) print(hex(compute_program_hash_chain(program)))
def test_main_scope(): identifiers = IdentifierManager.from_dict({ ScopedName.from_string('a.b'): ConstDefinition(value=1), ScopedName.from_string('x.y.z'): ConstDefinition(value=2), }) reference_manager = ReferenceManager() program = Program( prime=0, data=[], hints={}, builtins=[], main_scope=ScopedName.from_string('a'), identifiers=identifiers, reference_manager=reference_manager) # Check accessible identifiers. assert program.get_identifier('b', ConstDefinition) # Ensure inaccessible identifiers. with pytest.raises(MissingIdentifierError, match="Unknown identifier 'a'."): program.get_identifier('a.b', ConstDefinition) with pytest.raises(MissingIdentifierError, match="Unknown identifier 'x'."): program.get_identifier('x.y', ConstDefinition) with pytest.raises(MissingIdentifierError, match="Unknown identifier 'y'."): program.get_identifier('y', ConstDefinition)
async def tx_status(args, command_args): parser = argparse.ArgumentParser( description='Queries the status of a transaction given its ID.') parser.add_argument('--id', type=int, required=True, help='The ID of the transaction to query.') parser.add_argument( '--contract', type=argparse.FileType('r'), required=False, help='An optional path to the compiled contract with debug information. ' 'If given, the contract will be used to add location information to errors.' ) parser.add_argument('--error_message', action='store_true', help='Only print the error message.') parser.parse_args(command_args, namespace=args) feeder_gateway_client = get_feeder_gateway_client(args) tx_status_response = await feeder_gateway_client.get_transaction_status( tx_id=args.id) # Print the error message with reconstructed location information in traceback, if necessary. has_error_message = ('tx_failure_reason' in tx_status_response and 'error_message' in tx_status_response['tx_failure_reason']) error_message = '' if has_error_message: error_message = tx_status_response['tx_failure_reason'][ 'error_message'] if args.contract is not None: program_json = json.load(args.contract)['program'] error_message = reconstruct_traceback( program=Program.load(program_json), traceback_txt=error_message) tx_status_response['tx_failure_reason'][ 'error_message'] = error_message if args.error_message: print(error_message) else: print(json.dumps(tx_status_response, indent=4, sort_keys=True))
def assemble(preprocessed_program: PreprocessedProgram, main_scope: ScopedName = ScopedName(), add_debug_info: bool = False, file_contents_for_debug_info: Dict[str, str] = {}) -> Program: data: List[int] = [] hints: Dict[int, CairoHint] = {} debug_info = DebugInfo(instruction_locations={}, file_contents=file_contents_for_debug_info) \ if add_debug_info else None for inst in preprocessed_program.instructions: if inst.hint: hints[len(data)] = CairoHint( code=inst.hint.hint_code, accessible_scopes=inst.accessible_scopes, flow_tracking_data=inst.flow_tracking_data) if debug_info is not None and inst.instruction.location is not None: hint_location = None if inst.hint is not None and inst.hint.location is not None: hint_location = HintLocation( location=inst.hint.location, n_prefix_newlines=inst.hint.n_prefix_newlines, ) debug_info.instruction_locations[len(data)] = \ InstructionLocation( inst=inst.instruction.location, hint=hint_location, accessible_scopes=inst.accessible_scopes, flow_tracking_data=inst.flow_tracking_data) data += [ word for word in encode_instruction(build_instruction(inst.instruction), prime=preprocessed_program.prime) ] return Program(prime=preprocessed_program.prime, data=data, hints=hints, main_scope=main_scope, identifiers=preprocessed_program.identifiers, builtins=preprocessed_program.builtins, reference_manager=preprocessed_program.reference_manager, debug_info=debug_info)
def main(): parser = argparse.ArgumentParser( description= 'A tool to reconstruct Cairo traceback given a compiled program with debug ' 'information.') parser.add_argument('-v', '--version', action='version', version=f'%(prog)s {__version__}') parser.add_argument('--program', type=str, help='A path to the Cairo program.') parser.add_argument('--contract', type=str, help='A path to the StarkNet contract.') parser.add_argument( '--traceback', type=str, required=True, help= 'A path to the traceback file with the missing location information. ' 'Use "-" to read the traceback from stdin.') args = parser.parse_args() assert (0 if args.program is None else 1) + (0 if args.contract is None else 1) == 1, \ 'Exactly one of --program, --contract must be specified.' if args.program is not None: program_json = json.load(open(args.program)) else: assert args.contract is not None program_json = json.load(open(args.contract))['program'] program = Program.load(program_json) traceback = (open(args.traceback) if args.traceback != '-' else sys.stdin).read() print(reconstruct_traceback(program, traceback)) return 0
def main(): start_time = time.time() parser = argparse.ArgumentParser( description='A tool to compile Cairo code.') parser.add_argument('files', metavar='file', type=str, nargs='+', help='File names') parser.add_argument('--prime', type=int, default=DEFAULT_PRIME, help='The size of the finite field.') parser.add_argument( '--cairo_path', type=str, default='', help= ('A list of directories, separated by ":" to resolve import paths. ' 'The full list will consist of directories defined by this argument, followed by ' f'the environment variable {LIBS_DIR_ENVVAR}, the working directory and the standard ' 'library path.')) parser.add_argument( '--preprocess', action='store_true', help= 'Stop after the preprocessor step and output the preprocessed program.' ) parser.add_argument('--output', type=argparse.FileType('w'), help='The output file name (default: stdout).') parser.add_argument('--no_debug_info', dest='debug_info', action='store_false', help='Include debug information.') parser.add_argument( '--debug_info_with_source', action='store_true', help='Include debug information with a copy of the source code.') parser.add_argument( '--simple', action='store_true', help='Compile the program without adding additional code. ' 'In particular, program starts at the __start__ label, instead of the main() function.' ) parser.add_argument( '--cairo_dependencies', type=str, help= 'Output a list of the Cairo source files used during the compilation as a CMake file.' ) args = parser.parse_args() debug_info = args.debug_info or args.debug_info_with_source source_files = set() erred: bool = False try: codes = get_codes(args.files) if not args.simple: codes = add_start_code(codes) out = args.output if args.output is not None else sys.stdout cairo_path = list( filter( None, args.cairo_path.split(':') + os.getenv(LIBS_DIR_ENVVAR, '').split(':'))) if args.preprocess: module_reader = get_module_reader(cairo_path) preprocessed = preprocess_codes(codes, args.prime, module_reader.read, MAIN_SCOPE) source_files = module_reader.source_files print(preprocessed.format(), end='', file=out) else: program, source_files = compile_cairo_extended(codes, args.prime, cairo_path, debug_info, simple=args.simple) if args.debug_info_with_source: for source_file in source_files | set(args.files): program.debug_info.file_contents[source_file] = open( source_file).read() json.dump(Program.Schema().dump(program), out, indent=4, sort_keys=True) # Print a new line at the end. print(file=out) except LocationError as err: print(err, file=sys.stderr) erred = True if args.cairo_dependencies: generate_cairo_dependencies_file(args.cairo_dependencies, source_files | set(args.files), start_time) return 1 if erred else 0
def test_constants(program: Program): assert program.get_const('ADDR_BOUND') % DEFAULT_PRIME == ADDR_BOUND assert program.get_const('MAX_STORAGE_ITEM_SIZE') == MAX_STORAGE_ITEM_SIZE
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