def deduce_op1( self, instruction: Instruction, dst: Optional[MaybeRelocatable], op0: Optional[MaybeRelocatable]) -> \ Tuple[Optional[MaybeRelocatable], Optional[MaybeRelocatable]]: if instruction.opcode is Instruction.Opcode.ASSERT_EQ: if (instruction.res is Instruction.Res.OP1) and (dst is not None): return dst, dst elif (instruction.res is Instruction.Res.ADD) and (dst is not None) and \ (op0 is not None): return (dst - op0) % self.prime, dst # type: ignore elif (instruction.res is Instruction.Res.MUL) and isinstance(dst, int) and \ isinstance(op0, int) and op0 != 0: return div_mod(dst, op0, self.prime), dst return None, None
def deduce_op0( self, instruction: Instruction, dst: Optional[MaybeRelocatable], op1: Optional[MaybeRelocatable]) -> \ Tuple[Optional[MaybeRelocatable], Optional[MaybeRelocatable]]: if instruction.opcode is Instruction.Opcode.CALL: return self.run_context.pc + instruction.size, None elif instruction.opcode is Instruction.Opcode.ASSERT_EQ: if (instruction.res is Instruction.Res.ADD) and (dst is not None) and \ (op1 is not None): return (dst - op1) % self.prime, dst # type: ignore elif (instruction.res is Instruction.Res.MUL) and isinstance(dst, int) and \ isinstance(op1, int) and op1 != 0: return div_mod(dst, op1, self.prime), dst return None, None
def test_div_mod(): assert div_mod(2, 3, 5) == 4 with pytest.raises(AssertionError): div_mod(8, 10, 5)
def __init__( self, program: ProgramBase, run_context: RunContext, hint_locals: dict, static_locals: dict = {}, builtin_runners: Dict[str, BuiltinRunner] = {}, program_base: Optional[int] = None): """ hints - a dictionary from memory addresses to an executable object. When the pc points to the memory address, before the execution of the instruction, the executable object will be run. Executable objects are anything that can be placed inside exec. For example, 'a=5', or compile('a=5'). hint_locals - dictionary holding local values for execution of hints. Passed as locals parameter for the exec function. static_locals - dictionary holding static values for execution. They are available in all scopes. program_base - The pc of the first instruction in program (default is run_context.pc). """ self.prime = program.prime self.builtin_runners = builtin_runners self.exec_scopes: List[dict] = [] self.enter_scope(dict(hint_locals)) self.run_context = copy.copy(run_context) # Shallow copy. self.hints: Dict[MaybeRelocatable, CompiledHint] = {} # A map from hint index to pc. self.hint_pcs: Dict[int, MaybeRelocatable] = {} self.instruction_debug_info: Dict[MaybeRelocatable, InstructionLocation] = {} self.debug_file_contents: Dict[str, str] = {} self.program = program self.program_base = program_base if program_base is not None else self.run_context.pc self.validated_memory = ValidatedMemoryDict(memory=self.run_context.memory) # If program is a StrippedProgram, there are no hints or debug information to load. if isinstance(program, Program): self.load_program( program=program, program_base=self.program_base, ) self.trace: List[TraceEntry[MaybeRelocatable]] = [] # auto_deduction contains a mapping from a memory segment index to a list of functions # (and a tuple of additional arguments) that may try to automatically deduce the value # of memory cells in the segment (based on other memory cells). self.auto_deduction: Dict[int, List[Tuple[Rule, tuple]]] = {} # Current step. self.current_step = 0 # This flag can be set to true by hints to avoid the execution of the current step in # step() (so that only the hint will be performed, but nothing else will happen). self.skip_instruction_execution = False from starkware.python import math_utils self.static_locals = static_locals.copy() self.static_locals.update({ 'PRIME': self.prime, 'fadd': lambda a, b, p=self.prime: (a + b) % p, 'fsub': lambda a, b, p=self.prime: (a - b) % p, 'fmul': lambda a, b, p=self.prime: (a * b) % p, 'fdiv': lambda a, b, p=self.prime: math_utils.div_mod(a, b, p), 'fpow': lambda a, b, p=self.prime: pow(a, b, p), 'fis_quad_residue': lambda a, p=self.prime: math_utils.is_quad_residue(a, p), 'fsqrt': lambda a, p=self.prime: math_utils.sqrt(a, p), 'safe_div': math_utils.safe_div, })
def visit_ExprOperator(self, expr: ExprOperator): a = self.visit(expr.a) b = self.visit(expr.b) op = expr.op if isinstance(b, ExprConst) and op == '/' and b.val == 0: raise SimplifierError('Division by zero.', location=b.location) if isinstance(a, ExprConst) and isinstance(b, ExprConst): val = None if op == '/' and self.prime is not None: if b.val % self.prime == 0: raise SimplifierError('Division by zero.', location=b.location) val = div_mod(a.val, b.val, self.prime) if op != '/': val = self._to_field_element(OPERATOR_DICT[op](a.val, b.val)) if val is not None: return ExprConst(val, location=expr.location) if isinstance(a, ExprConst) and op == '+': assert not isinstance(b, ExprConst) # Move constant expression to the right. E.g., "5 + fp" -> "fp + 5" a, b = b, a if isinstance(b, ExprConst) and op == '-': # Replace x - y with x + (-y) for constant y. op = '+' b = ExprConst(val=self._to_field_element(-b.val), location=b.location) if isinstance(b, ExprConst) and op == '/' and self.prime is not None: # Replace x / y with x * (1/y) for constant y. op = '*' if b.val % self.prime == 0: raise SimplifierError('Division by zero.', location=b.location) inv_val = div_mod(1, b.val, self.prime) b = ExprConst(val=self._to_field_element(inv_val), location=b.location) if isinstance(b, ExprConst) and b.val == 0 and op in ['+', '-']: # Replace x + 0 and x - 0 by x. return a if isinstance(b, ExprConst) and b.val == 1 and op in ['*', '/']: # Replace x * 1 and x / 1 by x. return a if isinstance(a, ExprConst) and a.val == 1 and op == '*': # Replace 1 * x by x. return b if isinstance(b, ExprConst) and isinstance(a, ExprOperator) and \ ((op == '+' and a.op in ['+', '-']) or (op == '*' and a.op == '*')): # If the expression is of the form "(a + b) + c" where c is constant, change it to # "a + (b + c)", this allows compiling expressions of the form: "[fp + x + y]". # Rotate right. return self.visit(ExprOperator( a=a.a, op=a.op, b=ExprOperator( a=a.b, op=a.op, b=b, location=expr.location), location=expr.location)) return ExprOperator(a=a, op=op, b=b, location=expr.location)