示例#1
0
def test_main_args_match_builtins():
    """
    Checks that an appropriate exception is thrown if the arguments in a given Cairo program's main
    don't match the list of builtins specified by the directive (and their order).
    """
    expected_error_msg = 'Expected main to contain the following arguments (in this order): ' \
        "['output_ptr', 'range_check_ptr']"
    with pytest.raises(AssertionError, match=re.escape(expected_error_msg)):
        compile_cairo(code="""
%builtins output range_check

func main(output_ptr) -> (output_ptr):
    return (output_ptr=output_ptr + 1)
end
""",
                      prime=PRIME)

    # Check that even if all builtin ptrs were passed as arguments but in the wrong order then
    # the same exception is thrown.
    with pytest.raises(AssertionError, match=re.escape(expected_error_msg)):
        compile_cairo(code="""
%builtins output range_check

func main(range_check_ptr, output_ptr) -> (range_check_ptr, output_ptr):
    return (range_check_ptr + 1, output_ptr=output_ptr + 1)
end
""",
                      prime=PRIME)
def test_secure_hints_serialization():
    template_program = compile_cairo(ALLOWED_CODE, DEFAULT_PRIME)
    whitelist = HintsWhitelist.from_program(template_program)
    data = HintsWhitelist.Schema().dumps(whitelist)
    whitelist = HintsWhitelist.Schema().loads(data)
    for good_code in GOOD_CODES:
        program = compile_cairo(good_code, DEFAULT_PRIME)
        whitelist.verify_program_hint_secure(program)
def test_secure_hints_cases():
    template_program = compile_cairo(ALLOWED_CODE, DEFAULT_PRIME)
    whitelist = HintsWhitelist.from_program(template_program)
    for good_code in GOOD_CODES:
        program = compile_cairo(good_code, DEFAULT_PRIME)
        whitelist.verify_program_hint_secure(program)
    for bad_code, message in BAD_CODES:
        program = compile_cairo(bad_code, DEFAULT_PRIME)
        with pytest.raises(InsecureHintError, match=re.escape(message)):
            whitelist.verify_program_hint_secure(program)
示例#4
0
def test_main_return_match_builtins():
    """
    Checks that an appropriate exception is thrown if the arguments in a given Cairo program's main
    don't match the list of builtins specified by the directive (and their order).
    """
    expected_error_msg = 'Expected main to return the following values (in this order): ' \
        "['output_ptr', 'range_check_ptr']"
    with pytest.raises(AssertionError, match=re.escape(expected_error_msg)):
        compile_cairo(code="""
%builtins output range_check

func main(output_ptr, range_check_ptr) -> (output_ptr):
    return (output_ptr=output_ptr + 1)
end
""",
                      prime=PRIME)
示例#5
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
示例#6
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
示例#7
0
def get_runner_from_code(code: Union[str, Sequence[Tuple[str, str]]],
                         layout: str, prime: int) -> CairoRunner:
    """
    Given a code with some compile and run parameters (prime, layout, etc.), runs the code using
    Cairo runner and returns the runner.
    """
    program = compile_cairo(code=code, prime=prime, debug_info=True)
    return get_main_runner(program=program, hint_locals={}, layout=layout)
示例#8
0
def test_reconstruct_traceback():
    code = """
    func bar():
        assert 0 = 1
        return ()
    end

    func foo():
        bar()
        return ()
    end

    func main():
        foo()
        return ()
    end
    """
    codes = [(code, 'filename')]
    program_with_debug_info = compile_cairo(code=codes,
                                            prime=DEFAULT_PRIME,
                                            debug_info=True)
    program_without_debug_info = compile_cairo(code=codes,
                                               prime=DEFAULT_PRIME,
                                               debug_info=False)

    with pytest.raises(VmException) as exc:
        get_main_runner(program=program_without_debug_info,
                        hint_locals={},
                        layout='plain')

    exception_str = str(exc.value)

    # The exception before calling reconstruct_traceback().
    assert exception_str == """\
Error at pc=0:2:
An ASSERT_EQ instruction failed: 1 != 0
Cairo traceback (most recent call last):
Unknown location (pc=0:8)
Unknown location (pc=0:5)\
"""

    res = reconstruct_traceback(program=program_with_debug_info,
                                traceback_txt=exception_str)
    # The exception after calling reconstruct_traceback().
    assert res == """\
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')
示例#10
0
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
示例#11
0
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()
示例#12
0
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
示例#13
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_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)
示例#15
0
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
示例#16
0
def get_runner_from_code(code: Union[str, Sequence[Tuple[str, str]]],
                         layout: str, prime: int) -> CairoRunner:
    """
    Given a code with some compile and run parameters (prime, layout, etc.), runs the code using
    Cairo runner and returns the runner.
    """
    program = compile_cairo(code=code, prime=prime, debug_info=True)
    runner = CairoRunner(program, layout=layout)
    runner.initialize_segments()
    end = runner.initialize_main_entrypoint()
    runner.initialize_vm(hint_locals={})
    runner.run_until_pc(end)
    runner.read_return_values()
    runner.finalize_segments_by_effective_size()
    runner.end_run()
    return runner
示例#17
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()
示例#18
0
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()
示例#19
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
示例#20
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()
示例#21
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()
示例#22
0
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 test_collision():
    """
    Tests multiple hints with the same code but different reference expressions.
    """
    code = """
func f():
    let b = [ap]
    %{
        ids.b = 1
    %}
    ret
end
func g():
    let b = [ap - 10]
    %{
        ids.b = 1
    %}
    ret
end
"""
    program = compile_cairo(code, DEFAULT_PRIME)
    whitelist = HintsWhitelist.from_program(program)
    assert len(whitelist.allowed_reference_expressions_for_hint) == 1
    whitelist.verify_program_hint_secure(program)
示例#24
0
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'
    }