예제 #1
0
def test_memory_dict_serialize():
    memory = MemoryDict({1: 2, 3: 4, 5: 6})
    expected_serialized = bytes([
        1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0,
        6, 0, 0])
    serialized = memory.serialize(3)
    assert expected_serialized == serialized
    assert MemoryDict.deserialize(serialized, 3) == memory
예제 #2
0
def test_memory_dict_setdefault():
    md = MemoryDict({14: 15})
    md.setdefault(14, 0)
    assert md[14] == 15
    md.setdefault(123, 456)
    assert md[123] == 456
    with pytest.raises(ValueError, match='must be an int'):
        md.setdefault(10, 'default')
    with pytest.raises(ValueError, match='must be positive'):
        md.setdefault(-10, 123)
예제 #3
0
def test_memory_dict_setdefault():
    memory = MemoryDict({14: 15})
    memory.setdefault(14, 0)
    assert memory[14] == 15
    memory.setdefault(123, 456)
    assert memory[123] == 456
    with pytest.raises(ValueError, match='must be an int'):
        memory.setdefault(10, 'default')
    with pytest.raises(KeyError, match='must be nonnegative'):
        memory.setdefault(-10, 123)
    with pytest.raises(ValueError, match='The offset of a relocatable value must be nonnegative'):
        memory[RelocatableValue(segment_index=10, offset=-2)] = 13
def test_relocate_segments():
    segments = MemorySegmentManager(memory=MemoryDict({}), prime=PRIME)

    for i in range(5):
        assert segments.add() == RelocatableValue(segment_index=i, offset=0)

    segment_sizes = [3, 8, 0, 1, 2]
    public_memory_offsets = [
        [(0, 0), (1, 1)],
        [(i, 0) for i in range(8)],
        [],
        [],
        [(1, 2)],
    ]

    # These segments are not finalized.
    assert segments.add() == RelocatableValue(segment_index=5, offset=0)
    assert segments.add() == RelocatableValue(segment_index=6, offset=0)
    segments.memory[RelocatableValue(5, 4)] = 0

    segments.memory.freeze()
    segments.compute_effective_sizes()
    for i, (size, public_memory) in enumerate(
            zip(segment_sizes, public_memory_offsets)):
        segments.finalize(i, size=size, public_memory=public_memory)

    segment_offsets = segments.relocate_segments()
    assert segment_offsets == {0: 1, 1: 4, 2: 12, 3: 12, 4: 13, 5: 15, 6: 20}
    assert segments.get_public_memory_addresses(segment_offsets) == [(1, 0),
                                                                     (2, 1),
                                                                     (4, 0),
                                                                     (5, 0),
                                                                     (6, 0),
                                                                     (7, 0),
                                                                     (8, 0),
                                                                     (9, 0),
                                                                     (10, 0),
                                                                     (11, 0),
                                                                     (14, 2)]

    # Negative flows.
    segments = MemorySegmentManager(memory=MemoryDict({}), prime=PRIME)
    segments.add(size=1)
    with pytest.raises(AssertionError,
                       match='compute_effective_sizes must be called before'):
        segments.relocate_segments()

    segments.memory[RelocatableValue(0, 2)] = 0
    segments.memory.freeze()
    segments.compute_effective_sizes()
    with pytest.raises(AssertionError,
                       match='Segment 0 exceeded its allocated size'):
        segments.relocate_segments()
def test_get_segment_used_size():
    memory = MemoryDict({
        RelocatableValue(0, 0): 0,
        RelocatableValue(0, 2): 0,
        RelocatableValue(1, 5): 0,
        RelocatableValue(1, 7): 0,
        RelocatableValue(3, 0): 0,
        RelocatableValue(4, 1): 0,
    })
    segments = MemorySegmentManager(memory=memory, prime=PRIME)
    segments.n_segments = 5
    memory.freeze()
    segments.compute_effective_sizes()
    assert [segments.get_segment_used_size(i)
            for i in range(5)] == [3, 8, 0, 1, 2]
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
예제 #7
0
def write_binary_memory(memory_file: BinaryIO, memory: MemoryDict,
                        field_bytes: int):
    """
    Dumps the memory file.
    """
    memory_file.write(memory.serialize(field_bytes))
    memory_file.flush()
예제 #8
0
    def from_file(cls, fileobj) -> 'CairoPie':
        """
        Loads an instance of CairoPie from a file.
        `fileobj` can be a path or a file object.
        """

        if isinstance(fileobj, str):
            fileobj = open(fileobj, 'rb')

        verify_zip_file_prefix(fileobj=fileobj)

        with zipfile.ZipFile(fileobj) as zf:
            cls.verify_zip_format(zf)

            with zf.open(cls.METADATA_FILENAME, 'r') as fp:
                metadata = CairoPieMetadata.Schema().load(
                    json.loads(fp.read(cls.MAX_SIZE).decode('ascii')))
            with zf.open(cls.MEMORY_FILENAME, 'r') as fp:
                memory = MemoryDict.deserialize(
                    data=fp.read(cls.MAX_SIZE),
                    field_bytes=metadata.field_bytes,
                )
            with zf.open(cls.ADDITIONAL_DATA_FILENAME, 'r') as fp:
                additional_data = json.loads(
                    fp.read(cls.MAX_SIZE).decode('ascii'))
            with zf.open(cls.EXECUTION_RESOURCES_FILENAME, 'r') as fp:
                execution_resources = ExecutionResources.Schema().load(
                    json.loads(fp.read(cls.MAX_SIZE).decode('ascii')))
        return cls(metadata, memory, additional_data, execution_resources)
예제 #9
0
def run_single(code: str,
               steps: int,
               *,
               pc=RelocatableValue(0, 10),
               ap=100,
               fp=100,
               extra_mem={}):
    program = compile_cairo(code, PRIME, debug_info=True)

    # Set memory[fp - 1] to an arbitrary value, since [fp - 1] is assumed to be set.
    memory: Dict[MaybeRelocatable, MaybeRelocatable] = {
        **{pc + i: v
           for i, v in enumerate(program.data)}, fp - 1: 1234,
        **extra_mem
    }
    context = RunContext(
        pc=pc,
        ap=ap,
        fp=fp,
        memory=MemoryDict(memory),
        prime=PRIME,
    )

    vm = VirtualMachine(program, context, {})
    for _ in range(steps):
        vm.step()
    return vm
예제 #10
0
def test_jmp_segment():
    code = """
    jmp abs [ap]; ap++
    """
    program = compile_cairo(code=code, prime=PRIME, debug_info=True)

    program_base_a = RelocatableValue(0, 10)
    program_base_b = RelocatableValue(1, 20)

    memory = {
        **{program_base_a + i: v
           for i, v in enumerate(program.data)},
        **{program_base_b + i: v
           for i, v in enumerate(program.data)}, 99: 0,
        100: program_base_b,
        101: program_base_a
    }
    context = RunContext(
        pc=program_base_a,
        ap=100,
        fp=100,
        memory=MemoryDict(memory),
        prime=PRIME,
    )

    vm = VirtualMachine(program, context, {})
    vm.step()
    assert vm.run_context.pc == program_base_b
    assert vm.get_location(vm.run_context.pc) is None
    vm.step()
    assert vm.run_context.pc == program_base_a
    assert vm.get_location(vm.run_context.pc) is not None
예제 #11
0
def read_memory(memory_path: str, field_bytes: int) -> MemoryDict:
    """
    Returns the memory (as a MemoryDict).
    """
    # Use MemoryDict to verify that memory cells are consistent.
    with open(memory_path, 'rb') as memory_file:
        return MemoryDict.deserialize(memory_file.read(), field_bytes)
예제 #12
0
def test_memory_dict_get():
    md = MemoryDict({14: 15})
    assert md.get(14, 'default') == 15
    assert md.get(1234, 'default') == 'default'
    with pytest.raises(ValueError, match='must be nonnegative'):
        md.get(-10, 'default')
    # Attempting to read address with a negative offset is ok, it simply returns None.
    assert md.get(RelocatableValue(segment_index=10, offset=-2)) is None
예제 #13
0
def test_get_segment_used_size():
    memory = MemoryDict({
        RelocatableValue(0, 0): 0,
        RelocatableValue(0, 2): 0,
        RelocatableValue(1, 5): 0,
        RelocatableValue(1, 7): 0,
        RelocatableValue(3, 0): 0,
        RelocatableValue(4, 1): 0,
    })
    assert [get_segment_used_size(i, memory) for i in range(5)] == [3, 8, 0, 1, 2]
예제 #14
0
def test_segment_relocation_failures():
    memory = MemoryDict()

    relocation_target = RelocatableValue(segment_index=4, offset=25)
    with pytest.raises(AssertionError, match='src_ptr.segment_index must be < 0, src_ptr=1:2.'):
        memory.add_relocation_rule(src_ptr=RelocatableValue(
            segment_index=1, offset=2), dest_ptr=relocation_target)

    with pytest.raises(AssertionError, match='src_ptr.offset must be 0, src_ptr=-3:2.'):
        memory.add_relocation_rule(src_ptr=RelocatableValue(
            segment_index=-3, offset=2), dest_ptr=relocation_target)

    memory.add_relocation_rule(src_ptr=RelocatableValue(
        segment_index=-3, offset=0), dest_ptr=relocation_target)

    with pytest.raises(
            AssertionError, match='The segment with index -3 already has a relocation rule.'):
        memory.add_relocation_rule(src_ptr=RelocatableValue(
            segment_index=-3, offset=0), dest_ptr=relocation_target)
예제 #15
0
def test_memory_dict_check_element():
    memory = MemoryDict()
    with pytest.raises(KeyError, match='must be an int'):
        memory['not a number'] = 12
    with pytest.raises(KeyError, match='must be nonnegative'):
        memory[-12] = 13
    with pytest.raises(ValueError, match='The offset of a relocatable value must be nonnegative'):
        memory[RelocatableValue(segment_index=10, offset=-2)] = 13
    # A value may have a negative offset.
    memory[13] = RelocatableValue(segment_index=10, offset=-2)
예제 #16
0
def test_memory_dict():
    d = {1: 2}
    mem = MemoryDict(d)
    d[2] = 3
    assert 2 not in mem

    assert mem[1] == 2
    with pytest.raises(UnknownMemoryError):
        mem[2]
    mem[1] = 2
    with pytest.raises(InconsistentMemoryError):
        mem[1] = 3
예제 #17
0
def test_gen_args():
    segments = MemorySegmentManager(memory=MemoryDict({}), prime=PRIME)

    test_array = [2, 3, 7]
    arg = [1, test_array, [4, -1]]
    ptr = segments.gen_arg(arg)

    memory = segments.memory

    assert memory[ptr] == 1
    memory.get_range(memory[ptr + 1], len(test_array)) == test_array
    memory.get_range(memory[ptr + 2], 2) == [4, PRIME - 1]
예제 #18
0
    def relocate(self):
        self.segment_offsets = self.segments.relocate_segments()

        self.relocated_memory = MemoryDict({
            self.relocate_value(addr): self.relocate_value(value)
            for addr, value in self.vm_memory.items()
        })
        self.relocated_trace = relocate_trace(self.vm.trace,
                                              self.segment_offsets,
                                              self.program.prime)
        for builtin_runner in self.builtin_runners.values():
            builtin_runner.relocate(self.relocate_value)
예제 #19
0
def test_memory_dict_get():
    memory = MemoryDict({14: 15})
    assert memory.get(14, 'default') == 15
    assert memory.get(1234, 'default') == 'default'
    assert memory.get(-10, 'default') == 'default'
    # Attempting to read address with a negative offset is ok, it simply returns None.
    assert memory.get(RelocatableValue(segment_index=10, offset=-2)) is None
예제 #20
0
def get_segment_used_size(segment_index: int, memory: MemoryDict) -> int:
    """
    Returns the used size of the given memory segment by finding which is the maximal offset that
    was accessed.
    """
    max_offset = -1
    for addr in memory.keys():
        assert isinstance(addr, RelocatableValue), \
            f'Expected memory address to be relocatable value. Found: {addr}.'
        if addr.segment_index != segment_index:
            continue
        max_offset = max(max_offset, addr.offset)
    return max_offset + 1
예제 #21
0
def test_hint_exception():
    code = """
# Some comment.

%{ x = 0 %}

%{
def f():
    0 / 0  # Raises exception.
%}
[ap] = 0; ap++

%{ y = 0 %}
%{


f()
%}
[ap] = 1; ap++
"""

    # In this test we actually do write the code to a file, to allow the linecache module to fetch
    # the line raising the exception.
    cairo_file = tempfile.NamedTemporaryFile('w')
    print(code, file=cairo_file)
    cairo_file.flush()
    program = compile_cairo(code=[(code, cairo_file.name)],
                            prime=PRIME,
                            debug_info=True)
    program_base = 10
    memory = {program_base + i: v for i, v in enumerate(program.data)}

    # Set memory[fp - 1] to an arbitrary value, since [fp - 1] is assumed to be set.
    memory[99] = 1234

    context = RunContext(
        pc=program_base,
        ap=200,
        fp=100,
        memory=MemoryDict(memory),
        prime=PRIME,
    )

    vm = VirtualMachine(program, context, {})

    vm.step()
    with pytest.raises(VmException) as excinfo:
        vm.step()
    assert str(excinfo.value) == f"""\
def test_validated_memory_dict():
    memory = MemoryDict()
    memory_validator = ValidatedMemoryDict(memory=memory)

    def rule_identical_pairs(mem, addr):
        """
        Validates that the values in address pairs (i, i+1), where i is even, are identical.
        """
        offset_diff = (-1) ** (addr.offset % 2)
        other_addr = RelocatableValue.from_tuple((addr.segment_index, addr.offset + offset_diff))
        if other_addr in mem:
            assert mem[addr] == mem[other_addr]
            return {addr, other_addr}
        return set()

    def rule_constant_value(mem, addr, constant):
        assert mem[addr] == constant, \
            f'Expected value in address {addr} to be {constant}, got {mem[addr]}.'
        return {addr}

    memory_validator.add_validation_rule(1, lambda memory, addr: set())
    memory_validator.add_validation_rule(2, lambda memory, addr: {addr})
    memory_validator.add_validation_rule(3, rule_identical_pairs)
    memory_validator.add_validation_rule(4, rule_constant_value, 0)

    addr0 = RelocatableValue.from_tuple((1, 0))
    addr1 = RelocatableValue.from_tuple((2, 0))
    addr2 = RelocatableValue.from_tuple((3, 0))
    addr3 = RelocatableValue.from_tuple((3, 1))
    addr4 = RelocatableValue.from_tuple((4, 0))

    # Test validated_addresses update.
    memory_validator[addr0] = 0
    assert memory_validator._ValidatedMemoryDict__validated_addresses == set()
    memory_validator[addr1] = 0
    assert memory_validator._ValidatedMemoryDict__validated_addresses == {addr1}
    # Test validation rule application.
    memory_validator[addr2] = 1
    assert memory_validator._ValidatedMemoryDict__validated_addresses == {addr1}
    memory_validator[addr3] = 1
    assert memory_validator._ValidatedMemoryDict__validated_addresses == {addr1, addr2, addr3}

    with pytest.raises(
            AssertionError, match='Expected value in address 4:0 to be 0, got 1.'):
        memory_validator[addr4] = 1
예제 #23
0
def test_skip_instruction_execution():
    code = """
%{
    x = 0
    vm.run_context.pc += 2
    vm.skip_instruction_execution = True
%}
[ap] = [ap] + 1; ap++ # This intruction will not be executed.
%{
    x = 1
%}
[ap] = 10; ap++
    """

    program = compile_cairo(code, PRIME, debug_info=True)

    initial_ap = 100
    memory: Dict[MaybeRelocatable, MaybeRelocatable] = {
        **{i: v
           for i, v in enumerate(program.data)},
        initial_ap - 1: 1234,
    }
    context = RunContext(
        pc=0,
        ap=initial_ap,
        fp=initial_ap,
        memory=MemoryDict(memory),
        prime=PRIME,
    )

    vm = VirtualMachine(program, context, {})
    vm.enter_scope({'vm': vm})
    exec_locals = vm.exec_scopes[-1]

    assert 'x' not in exec_locals
    assert vm.run_context.pc == 0
    vm.step()
    assert exec_locals['x'] == 0
    assert vm.run_context.pc == 2
    vm.step()
    assert exec_locals['x'] == 1
    assert vm.run_context.pc == 4
    assert vm.run_context.ap == initial_ap + 1
    assert vm.run_context.memory[vm.run_context.ap - 1] == 10
    vm.exit_scope()
예제 #24
0
def test_segment_relocations():
    memory = MemoryDict()

    temp_segment = RelocatableValue(segment_index=-1, offset=0)
    memory[5] = temp_segment + 2
    assert memory[5] == RelocatableValue(segment_index=-1, offset=2)
    relocation_target = RelocatableValue(segment_index=4, offset=25)
    memory.add_relocation_rule(src_ptr=temp_segment, dest_ptr=relocation_target)
    assert memory[5] == relocation_target + 2

    memory[temp_segment + 3] = 17
    memory.relocate_memory()
    assert memory.data == {
        5: relocation_target + 2,
        relocation_target + 3: 17,
    }
예제 #25
0
def test_cairo_pie_serialize_deserialize():
    program = compile_cairo(
        code=[('%builtins output pedersen range_check ecdsa\nmain:\n[ap] = [ap]\n', '')],
        prime=DEFAULT_PRIME)
    metadata = CairoPieMetadata(
        program=program.stripped(),
        program_segment=SegmentInfo(0, 10),
        execution_segment=SegmentInfo(1, 20),
        ret_fp_segment=SegmentInfo(6, 12),
        ret_pc_segment=SegmentInfo(7, 21),
        builtin_segments={
            'a': SegmentInfo(4, 15),
        },
        extra_segments=[],
    )
    memory = MemoryDict({
        1: 2,
        RelocatableValue(3, 4): RelocatableValue(6, 7),
    })
    additional_data = {'c': ['d', 3]}
    execution_resources = ExecutionResources(
        n_steps=10,
        n_memory_holes=7,
        builtin_instance_counter={
            'output': 6,
            'pedersen': 3,
        }
    )
    cairo_pie = CairoPie(
        metadata=metadata,
        memory=memory,
        additional_data=additional_data,
        execution_resources=execution_resources
    )

    fileobj = io.BytesIO()
    cairo_pie.to_file(fileobj)
    actual_cairo_pie = CairoPie.from_file(fileobj)

    assert cairo_pie == actual_cairo_pie
예제 #26
0
def test_memory_validation_in_hints():
    code = """
%{ memory[ap] = 0 %}
[ap] = [ap]; ap++
%{ memory[ap] = 0 %}
[ap] = [ap]; ap++
"""

    program = compile_cairo(code=code, prime=PRIME, debug_info=True)
    initial_ap_and_fp = RelocatableValue(segment_index=1, offset=200)
    memory = {i: v for i, v in enumerate(program.data)}
    # Set memory[fp - 1] to an arbitrary value, since [fp - 1] is assumed to be set.
    memory[initial_ap_and_fp - 1] = 1234

    context = RunContext(
        pc=0,
        ap=initial_ap_and_fp,
        fp=initial_ap_and_fp,
        memory=MemoryDict(memory),
        prime=PRIME,
    )

    vm = VirtualMachine(program, context, {})

    vm.add_validation_rule(1, lambda memory, addr: {addr})
    assert vm.validated_memory._ValidatedMemoryDict__validated_addresses == set(
    )
    vm.step()
    assert vm.validated_memory._ValidatedMemoryDict__validated_addresses == {
        initial_ap_and_fp
    }

    def fail_validation(memory, addr):
        raise Exception('Validation failed.')

    vm.add_validation_rule(1, fail_validation)
    with pytest.raises(VmException, match='Exception: Validation failed.'):
        vm.step()
예제 #27
0
def test_auto_deduction_rules():
    code = """
[fp + 1] = [fp] + [ap]
"""

    program = compile_cairo(code=code, prime=PRIME, debug_info=True)
    memory = {i: v for i, v in enumerate(program.data)}
    initial_ap = RelocatableValue(segment_index=1, offset=200)
    initial_fp = RelocatableValue(segment_index=2, offset=100)

    context = RunContext(
        pc=0,
        ap=initial_ap,
        fp=initial_fp,
        memory=MemoryDict(memory),
        prime=PRIME,
    )

    vm = VirtualMachine(program, context, {})

    def rule_ap_segment(vm, addr, val):
        return val

    vm.add_auto_deduction_rule(1, rule_ap_segment, 100)
    vm.add_auto_deduction_rule(2, lambda vm, addr: None)
    vm.add_auto_deduction_rule(
        2, lambda vm, addr: 200 if addr == initial_fp else None)
    vm.add_auto_deduction_rule(2, lambda vm, addr: 456)

    vm.step()

    assert vm.run_context.memory[initial_ap] == 100
    assert vm.run_context.memory[initial_fp] == 200
    assert vm.run_context.memory[initial_fp + 1] == 300

    with pytest.raises(InconsistentAutoDeductionError,
                       match='at address 2:100. 200 != 456'):
        vm.verify_auto_deductions()
예제 #28
0
def test_memory_dict_multiple_values():
    memory = MemoryDict({5: 10})
    memory[5] = 10
    memory[5] = 10
    with pytest.raises(InconsistentMemoryError):
        memory[5] = 11
예제 #29
0
def test_memory_dict_in():
    memory = MemoryDict({1: 2, 3: 4})
    assert 1 in memory
    assert 2 not in memory
    # Test that `in` doesn't add the value to the dict.
    assert 2 not in memory
예제 #30
0
def test_memory_dict_getitem():
    memory = MemoryDict({11: 12})
    with pytest.raises(UnknownMemoryError):
        memory[12]