Exemplo n.º 1
0
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
Exemplo n.º 2
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)
Exemplo n.º 3
0
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)
Exemplo n.º 4
0
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)
Exemplo n.º 5
0
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)))
Exemplo n.º 6
0
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)
Exemplo n.º 7
0
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))
Exemplo n.º 8
0
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
Exemplo n.º 10
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
Exemplo n.º 11
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
Exemplo n.º 12
0
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