Example #1
0
    def __teal__(self, options: "CompileOptions"):
        if not isinstance(self.start, Int):
            return TernaryExpr(
                Op.replace3,
                (TealType.bytes, TealType.uint64, TealType.bytes),
                TealType.bytes,
                self.original,
                self.start,
                self.replacement,
            ).__teal__(options)

        op = self.__get_op(options)

        verifyProgramVersion(
            op.min_version,
            options.version,
            "Program version too low to use op {}".format(op),
        )

        s = cast(Int, self.start).value
        if op == Op.replace2:
            return TealBlock.FromOp(options, TealOp(self, op, s),
                                    self.original, self.replacement)
        elif op == Op.replace3:
            return TealBlock.FromOp(options, TealOp(self, op), self.original,
                                    self.start, self.replacement)
Example #2
0
    def __teal__(self, options: "CompileOptions"):
        if not isinstance(self.startArg, Int) or not isinstance(
                self.lenArg, Int):
            return TernaryExpr(
                Op.extract3,
                (TealType.bytes, TealType.uint64, TealType.uint64),
                TealType.bytes,
                self.stringArg,
                self.startArg,
                self.lenArg,
            ).__teal__(options)

        op = self.__get_op(options)

        verifyProgramVersion(
            op.min_version,
            options.version,
            "Program version too low to use op {}".format(op),
        )

        s, l = cast(Int, self.startArg).value, cast(Int, self.lenArg).value
        if op == Op.extract:
            return TealBlock.FromOp(options, TealOp(self, op, s, l),
                                    self.stringArg)
        elif op == Op.extract3:
            return TealBlock.FromOp(
                options,
                TealOp(self, op),
                self.stringArg,
                self.startArg,
                self.lenArg,
            )
Example #3
0
    def __teal__(self, options: "CompileOptions"):
        from pyteal.ir import TealOp, Op, TealBlock

        if self.index_expression is not None:
            op = TealOp(self, Op.loads)
            return TealBlock.FromOp(options, op, self.index_expression)

        s = cast(ScratchSlot, self.slot)
        op = TealOp(self, Op.load, s)
        return TealBlock.FromOp(options, op)
Example #4
0
    def __teal__(self, options: "CompileOptions"):
        from pyteal.ir import TealOp, Op, TealBlock

        if self.index_expression is not None:
            op = TealOp(self, Op.stores)
            return TealBlock.FromOp(options, op, self.index_expression, self.value)

        if not isinstance(self.slot, ScratchSlot):
            raise TealInternalError(
                "cannot handle slot of type {}".format(type(self.slot))
            )
        op = TealOp(self, Op.store, self.slot)
        return TealBlock.FromOp(options, op, self.value)
Example #5
0
    def __teal__(self, options: "CompileOptions"):
        verifyProgramVersion(
            Op.gaid.min_version,
            options.version,
            "Program version too low to use Gaid expression",
        )

        if type(self.txnIndex) is int:
            op = TealOp(self, Op.gaid, self.txnIndex)
            return TealBlock.FromOp(options, op)

        op = TealOp(self, Op.gaids)
        return TealBlock.FromOp(options, op, cast(Expr, self.txnIndex))
Example #6
0
    def __teal__(self, options: "CompileOptions"):
        if type(self.index) is int:
            op = TealOp(self, Op.arg, self.index)
            return TealBlock.FromOp(options, op)

        verifyProgramVersion(
            Op.args.min_version,
            options.version,
            "Program version too low to use dynamic indexes with Arg",
        )

        op = TealOp(self, Op.args)
        return TealBlock.FromOp(options, op, cast(Expr, self.index))
Example #7
0
    def __teal__(self, options: "CompileOptions"):
        op = self.__get_op(options)

        verifyProgramVersion(
            op.min_version,
            options.version,
            "Program version too low to use op {}".format(op),
        )

        if op == Op.extract:
            # if possible, exploit optimization in the extract opcode that takes the suffix
            # when the length argument is 0
            return TealBlock.FromOp(
                options,
                TealOp(self, op,
                       cast(Int, self.startArg).value, 0),
                self.stringArg,
            )
        elif op == Op.substring3:
            strBlockStart, strBlockEnd = self.stringArg.__teal__(options)
            nextBlockStart, nextBlockEnd = self.startArg.__teal__(options)
            strBlockEnd.setNextBlock(nextBlockStart)

            finalBlock = TealSimpleBlock([
                TealOp(self, Op.dig, 1),
                TealOp(self, Op.len),
                TealOp(self, Op.substring3),
            ])

            nextBlockEnd.setNextBlock(finalBlock)
            return strBlockStart, finalBlock
Example #8
0
def collect_unoptimized_slots(
    subroutineBlocks: Dict[Optional[SubroutineDefinition], TealBlock]
) -> Set[ScratchSlot]:
    """Find and return all referenced ScratchSlots that need to be skipped
    during optimization.

    Args:
        subroutineBlocks: A mapping from subroutine to the subroutine's control flow graph.
        The key None is taken to mean the main program routine.

    Returns:
        A set which contains the slots used by DynamicScratchVars, all the reserved slots,
            and all global slots.
    """

    unoptimized_slots: Set[ScratchSlot] = set()

    def collectSlotsFromBlock(block: TealBlock):
        for op in block.ops:
            for slot in op.getSlots():
                # dynamic slot or reserved slot
                if op.op == Op.int or slot.isReservedSlot:
                    unoptimized_slots.add(slot)

    for _, start in subroutineBlocks.items():
        for block in TealBlock.Iterate(start):
            collectSlotsFromBlock(block)

    global_slots, _ = collectScratchSlots(subroutineBlocks)
    unoptimized_slots.update(global_slots)
    return unoptimized_slots
Example #9
0
    def __teal__(self, options: "CompileOptions"):
        if len(self.cond) > 1:
            asserts: list[Expr] = []
            for cond in self.cond:
                asrt = Assert(cond, comment=self.comment)
                asrt.trace = cond.trace
                asserts.append(asrt)
            return Seq(*asserts).__teal__(options)

        if options.version >= Op.assert_.min_version:
            # use assert op if available
            conds: list[Expr] = [self.cond[0]]
            if self.comment is not None:
                conds.append(Comment(self.comment))
            return TealBlock.FromOp(options, TealOp(self, Op.assert_), *conds)

        # if assert op is not available, use branches and err
        condStart, condEnd = self.cond[0].__teal__(options)

        end = TealSimpleBlock([])
        errBlock = TealSimpleBlock([TealOp(self, Op.err)])

        branchBlock = TealConditionalBlock([])
        branchBlock.setTrueBlock(end)
        branchBlock.setFalseBlock(errBlock)

        condEnd.setNextBlock(branchBlock)

        return condStart, end
Example #10
0
    def __teal__(self, options: "CompileOptions"):
        verifyProgramVersion(
            self.op.min_version,
            options.version,
            "Program version too low to use op {}".format(self.op),
        )

        return TealBlock.FromOp(options, TealOp(self, self.op), self.arg)
Example #11
0
def _remove_extraneous_slot_access(start: TealBlock, remove: Set[ScratchSlot]):
    def keep_op(op: TealOp) -> bool:
        if type(op) != TealOp or (op.op != Op.store and op.op != Op.load):
            return True

        return not set(op.getSlots()).issubset(remove)

    for block in TealBlock.Iterate(start):
        block.ops = list(filter(keep_op, block.ops))
Example #12
0
 def __teal__(self, options: "CompileOptions"):
     if self.base == "utf8":
         payload = self.byte_str
     elif self.base == "base16":
         payload = "0x" + self.byte_str
     else:
         payload = "{}({})".format(self.base, self.byte_str)
     op = TealOp(self, Op.byte, payload)
     return TealBlock.FromOp(options, op)
Example #13
0
    def __teal__(self, options: "CompileOptions"):
        op = self.action.value

        verifyProgramVersion(
            op.min_version,
            options.version,
            "Program version too low to create inner transactions",
        )

        return TealBlock.FromOp(options, TealOp(self, op))
Example #14
0
    def __teal__(self, options: "CompileOptions"):
        verifyProgramVersion(
            Op.itxn_field.min_version,
            options.version,
            "Program version too low to create inner transactions",
        )

        return TealBlock.FromOp(
            options, TealOp(self, Op.itxn_field, self.field.arg_name), self.value
        )
Example #15
0
    def __teal__(self, options: "CompileOptions"):
        verifyFieldVersion(self.field.arg_name, self.field.min_version, options.version)
        verifyProgramVersion(
            self.op.min_version,
            options.version,
            "Program version too low to use op {}".format(self.op),
        )

        op = TealOp(self, self.op, self.field.arg_name)
        return TealBlock.FromOp(options, op)
Example #16
0
    def __teal__(self, options: "CompileOptions"):
        verifyFieldVersion(self.field.arg_name, self.field.min_version, options.version)

        opToUse = self.staticOp if type(self.index) is int else self.dynamicOp
        if opToUse is None:
            raise TealCompileError("Dynamic array indexing not supported", self)

        verifyProgramVersion(
            opToUse.min_version,
            options.version,
            "Program version too low to use op {}".format(opToUse),
        )

        if type(self.index) is int:
            op = TealOp(self, opToUse, self.field.arg_name, self.index)
            return TealBlock.FromOp(options, op)

        op = TealOp(self, opToUse, self.field.arg_name)
        return TealBlock.FromOp(options, op, cast(Expr, self.index))
Example #17
0
    def __teal__(self, options: "CompileOptions"):
        verifyFieldVersion(self.field.arg_name, self.field.min_version,
                           options.version)

        verifyProgramVersion(
            Op.gitxn.min_version,
            options.version,
            "Program version too low to use gitxn",
        )
        op = TealOp(self, Op.gitxn, self.txnIndex, self.field.arg_name)
        return TealBlock.FromOp(options, op)
Example #18
0
    def __teal__(self, options: "CompileOptions"):
        verifyFieldVersion(self.field.arg_name, self.field.min_version,
                           options.version)

        if type(self.txnIndex) is int:
            verifyProgramVersion(
                Op.gtxn.min_version,
                options.version,
                "Program version too low to use gtxn",
            )
            op = TealOp(self, Op.gtxn, self.txnIndex, self.field.arg_name)
            return TealBlock.FromOp(options, op)

        verifyProgramVersion(
            Op.gtxns.min_version,
            options.version,
            "Program version too low to index Gtxn with dynamic values",
        )

        op = TealOp(self, Op.gtxns, self.field.arg_name)
        return TealBlock.FromOp(options, op, cast(Expr, self.txnIndex))
Example #19
0
    def __teal__(self, options: "CompileOptions"):
        verifyProgramVersion(
            Op.json_ref.min_version,
            options.version,
            "Program version too low to use op json_ref",
        )

        verifyFieldVersion(self.type.arg_name, self.type.min_version,
                           options.version)

        op = TealOp(self, Op.json_ref, self.type.arg_name)
        return TealBlock.FromOp(options, op, self.json_obj, self.key)
Example #20
0
def _has_load_dependencies(cur_block: TealBlock, start: TealBlock,
                           slot: ScratchSlot, pos: int) -> bool:
    for block in TealBlock.Iterate(start):
        for i, op in enumerate(block.ops):
            if block == cur_block and i == pos:
                continue

            if type(op) == TealOp and op.op == Op.load and slot in set(
                    op.getSlots()):
                return True

    return False
Example #21
0
    def __teal__(self, options: "CompileOptions"):
        verifyFieldVersion(self.field.arg_name, self.field.min_version,
                           options.version)

        if type(self.index) is int:
            opToUse = Op.gitxna
        else:
            opToUse = Op.gitxnas

        verifyProgramVersion(
            opToUse.min_version,
            options.version,
            "Program version too low to use op {}".format(opToUse),
        )

        if type(self.index) is int:
            op = TealOp(self, opToUse, self.txnIndex, self.field.arg_name,
                        self.index)
            return TealBlock.FromOp(options, op)
        op = TealOp(self, opToUse, self.txnIndex, self.field.arg_name)
        return TealBlock.FromOp(options, op, cast(Expr, self.index))
Example #22
0
def apply_global_optimizations(start: TealBlock,
                               options: OptimizeOptions) -> TealBlock:
    # limit number of iterations to length of teal program to avoid potential
    # infinite loops.
    for block in TealBlock.Iterate(start):
        for _ in range(len(block.ops)):
            prev_ops = block.ops.copy()
            if options.scratch_slots:
                _apply_slot_to_stack(block, start, options._skip_slots)

            if prev_ops == block.ops:
                break

    return start
Example #23
0
def collectScratchSlots(
    subroutineBlocks: Dict[Optional[SubroutineDefinition], TealBlock]
) -> Tuple[Set[ScratchSlot], Dict[Optional[SubroutineDefinition],
                                  Set[ScratchSlot]]]:
    """Find and return all referenced ScratchSlots for each subroutine.

    Args:
        subroutineBlocks: A mapping from subroutine to the subroutine's control flow graph.
        The key None is taken to mean the main program routine.

    Returns:
        A tuple of a set containing all global slots and a dictionary whose keys are the
            same as subroutineBlocks, and whose values are the local slots of that
            subroutine.
    """

    subroutineSlots: Dict[Optional[SubroutineDefinition],
                          Set[ScratchSlot]] = dict()

    def collectSlotsFromBlock(block: TealBlock, slots: Set[ScratchSlot]):
        for op in block.ops:
            for slot in op.getSlots():
                slots.add(slot)

    for subroutine, start in subroutineBlocks.items():
        slots: Set[ScratchSlot] = set()
        for block in TealBlock.Iterate(start):
            collectSlotsFromBlock(block, slots)

        subroutineSlots[subroutine] = slots

    # all scratch slots referenced by more than 1 subroutine
    global_slots: Set[ScratchSlot] = set()

    # all scratch slots referenced by only 1 subroutine
    local_slots: Dict[Optional[SubroutineDefinition],
                      Set[ScratchSlot]] = dict()

    for subroutine, slots in subroutineSlots.items():
        allOtherSlots: Set[ScratchSlot] = set()

        for otherSubroutine, otherSubroutineSlots in subroutineSlots.items():
            if subroutine is not otherSubroutine:
                allOtherSlots |= otherSubroutineSlots

        global_slots |= slots & allOtherSlots
        local_slots[subroutine] = slots - global_slots

    return global_slots, local_slots
Example #24
0
    def __teal__(self, options: "CompileOptions"):
        self.compile_check(options)

        tealOp = TealOp(self, self.op, *self.immediate_args)
        callStart, callEnd = TealBlock.FromOp(options, tealOp, *self.args)

        curEnd = callEnd
        # the list is reversed in order to preserve the ordering of the opcode's returned
        # values. ie the output to stack [A, B, C] should correspond to C->output_slots[2]
        # B->output_slots[1], and A->output_slots[0].
        for slot in reversed(self.output_slots):
            store = slot.store()
            storeStart, storeEnd = store.__teal__(options)
            curEnd.setNextBlock(storeStart)
            curEnd = storeEnd

        return callStart, curEnd
Example #25
0
    def __teal__(self, options: "CompileOptions"):
        if options.currentSubroutine is not None:
            verifyProgramVersion(
                Op.retsub.min_version,
                options.version,
                "Program version too low to use subroutines",
            )
            returnType = options.currentSubroutine.return_type
            if returnType == TealType.none:
                if self.value is not None:
                    raise TealCompileError(
                        "Cannot return a value from a subroutine with return type TealType.none",
                        self,
                    )
            else:
                if self.value is None:
                    raise TealCompileError(
                        "A subroutine declares it returns a value, but no value is being returned",
                        self,
                    )
                actualType = self.value.type_of()
                if not types_match(actualType, returnType):
                    raise TealCompileError(
                        "Incompatible return type from subroutine, expected {} but got {}"
                        .format(returnType, actualType),
                        self,
                    )
            op = Op.retsub
        else:
            if self.value is None:
                raise TealCompileError(
                    "Return from main program must have an argument", self)
            actualType = self.value.type_of()
            if not types_match(actualType, TealType.uint64):
                raise TealCompileError(
                    "Incompatible return type from main program, expected {} but got {}"
                    .format(TealType.uint64, actualType),
                    self,
                )
            op = Op.return_

        args = [] if self.value is None else [self.value]
        return TealBlock.FromOp(options, TealOp(self, op), *args)
Example #26
0
    def __teal__(self, options: "CompileOptions"):
        """
        Generate the subroutine's start and end teal blocks.
        The subroutine's arguments are pushed on the stack to be picked up into local scratch variables.
        There are 4 cases to consider for the pushed arg expression:

        1. (by-value) In the case of typical arguments of type Expr, the expression ITSELF is evaluated for the stack
            and will be stored in a local ScratchVar for subroutine evaluation

        2. (by-reference) In the case of a by-reference argument of type ScratchVar, its SLOT INDEX is put on the stack
            and will be stored in a local DynamicScratchVar for subroutine evaluation

        3. (ABI, or a special case in by-value) In this case, the storage of an ABI value are loaded
            to the stack and will be stored in a local ABI value for subroutine evaluation

        4. (ABI output keyword argument) In this case, we do not place ABI values (encoding) on the stack.
            This is an *output-only* argument: in `evaluate_subroutine` an ABI typed instance for subroutine evaluation
            will be generated, and gets in to construct the subroutine implementation.
        """
        verifyProgramVersion(
            Op.callsub.min_version,
            options.version,
            "Program version too low to use SubroutineCall expression",
        )

        def handle_arg(arg: Expr | ScratchVar | abi.BaseType) -> Expr:
            if isinstance(arg, ScratchVar):
                return arg.index()
            elif isinstance(arg, Expr):
                return arg
            elif isinstance(arg, abi.BaseType):
                return arg.stored_value.load()
            else:
                raise TealInputError(
                    f"cannot handle current arg: {arg} to put it on stack"
                )

        op = TealOp(self, Op.callsub, self.subroutine)
        return TealBlock.FromOp(options, op, *[handle_arg(x) for x in self.args])
Example #27
0
 def __teal__(self, options: "CompileOptions"):
     op = TealOp(self, Op.err)
     return TealBlock.FromOp(options, op)
Example #28
0
 def __teal__(self, options: "CompileOptions"):
     return TealBlock.FromOp(options, TealOp(self, self.field.get_op()),
                             *self.args)
Example #29
0
 def __teal__(
         self,
         options: "CompileOptions") -> Tuple[TealBlock, TealSimpleBlock]:
     op = TealOp(self, Op.comment, self.comment)
     return TealBlock.FromOp(options, op)
Example #30
0
    def __teal__(self, options: "CompileOptions"):
        verifyFieldVersion(self.field.arg_name, self.field.min_version, options.version)

        op = TealOp(self, Op.global_, self.field.arg_name)
        return TealBlock.FromOp(options, op)