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
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
def initialize_vm(self, hint_locals, static_locals: Optional[Dict[str, Any]] = None, vm_class=VirtualMachine): context = RunContext( pc=self.initial_pc, ap=self.initial_ap, fp=self.initial_fp, memory=self.memory, prime=self.program.prime, ) if static_locals is None: static_locals = {} self.vm = vm_class( self.program, context, hint_locals=hint_locals, static_locals=dict(segments=self.segments, **static_locals), builtin_runners=self.builtin_runners, program_base=self.program_base, ) for builtin_runner in self.builtin_runners.values(): builtin_runner.add_validation_rules(self) builtin_runner.add_auto_deduction_rules(self) self.vm.validate_existing_memory()
def __init__(self, program: Program, memory: MemoryDict, trace: List[TraceEntry], air_public_input: Optional[PublicInput] = None, debug_info: Optional[DebugInfo] = None): self.program = program self.memory = memory self.trace = trace self.debug_info = debug_info if debug_info is not None else program.debug_info # Read AIR public input, if available and extract public memory addresses. if air_public_input is not None: self.public_memory: List[int] = [ x.address for x in air_public_input.public_memory ] else: self.public_memory = [] self.input_files: Dict[str, InputCodeFile] = {} if self.debug_info is not None: # Process each instruction in the program and surround it by a <span> tag. for pc, instruction_location in self.debug_info.instruction_locations.items( ): loc = instruction_location.inst filename = loc.input_file.filename # If filename was not loaded yet, create a new InputCodeFile instance. if filename not in self.input_files: self.input_files[filename] = InputCodeFile( loc.input_file.get_content()) input_file = self.input_files[filename] # Surround the instruction code with a <span> tag. input_file.mark_text(loc.start_line, loc.start_col, loc.end_line, loc.end_col, [f'inst{pc}', 'instruction']) # Find memory accesses for each step. self.memory_accesses = [] for trace_entry in self.trace: run_context = RunContext(pc=trace_entry.pc, ap=trace_entry.ap, fp=trace_entry.fp, memory=self.memory, prime=self.program.prime) instruction_encoding, imm = run_context.get_instruction_encoding() instruction = decode_instruction(instruction_encoding, imm) dst_addr = run_context.compute_dst_addr(instruction) op0_addr = run_context.compute_op0_addr(instruction) op1_addr = run_context.compute_op1_addr(instruction, self.memory.get(op0_addr)) self.memory_accesses.append({ 'dst': dst_addr, 'op0': op0_addr, 'op1': op1_addr })
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_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()
def initialize_vm(self, hint_locals, vm_class=VirtualMachine): context = RunContext( pc=self.initial_pc, ap=self.initial_ap, fp=self.initial_fp, memory=self.memory, prime=self.program.prime, ) self.vm = vm_class( self.program, context, hint_locals=hint_locals, static_locals=dict(segments=self.segments), builtin_runners=self.builtin_runners, program_base=self.program_base, ) for builtin_runner in self.builtin_runners.values(): builtin_runner.add_validation_rules(self) builtin_runner.add_auto_deduction_rules(self)
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()
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()