コード例 #1
0
 def __teal__(
         self,
         options: "CompileOptions") -> Tuple[TealBlock, TealSimpleBlock]:
     if options.version < Op.log.min_version:
         raise TealInputError(
             f"current version {options.version} is lower than log's min version {Op.log.min_version}"
         )
     return Log(Concat(Bytes(RETURN_HASH_PREFIX),
                       self.arg.encode())).__teal__(options)
コード例 #2
0
ファイル: nonce.py プロジェクト: algorand/pyteal
    def __init__(self, base: str, nonce: str, child: Expr) -> None:
        """Create a new Nonce.

        The Nonce expression behaves exactly like the child expression passed into it, except it
        uses the provided nonce string to alter its structure in a way that does not affect
        execution.

        Args:
            base: The base of the nonce. Must be one of utf8, base16, base32, or base64.
            nonce: An arbitrary nonce string that conforms to base.
            child: The expression to wrap.
        """
        super().__init__()

        if base not in ("utf8", "base16", "base32", "base64"):
            raise TealInputError("Invalid base: {}".format(base))

        self.child = child
        if base == "utf8":
            self.nonce_bytes = Bytes(nonce)
        else:
            self.nonce_bytes = Bytes(base, nonce)

        self.seq = Seq([Pop(self.nonce_bytes), self.child])
コード例 #3
0
ファイル: bool.py プロジェクト: algorand/pyteal
def _encode_bool_sequence(values: Sequence[Bool]) -> Expr:
    """Encoding a sequences of boolean values into a byte string.

    Args:
        values: The values to encode. Each must be an instance of Bool.

    Returns:
        An expression which creates an encoded byte string with the input boolean values.
    """
    length = _bool_sequence_length(len(values))
    expr: Expr = Bytes(b"\x00" * length)

    for i, value in enumerate(values):
        expr = SetBit(expr, Int(i), value.get())

    return expr
コード例 #4
0
    def set(
        self,
        values: Union[
            bytes,
            bytearray,
            Expr,
            Sequence[Byte],
            StaticArray[Byte, N],
            ComputedValue[StaticArray[Byte, N]],
        ],
    ) -> Expr:
        """Set the elements of this StaticBytes to the input values.

        The behavior of this method depends on the input argument type:

            * :code:`bytes`: set the value to the Python byte string.
            * :code:`bytearray`: set the value to the Python byte array.
            * :code:`Expr`: set the value to the result of a PyTeal expression, which must evaluate to a TealType.bytes.
            * :code:`Sequence[Byte]`: set the bytes of this String to those contained in this Python sequence (e.g. a list or tuple).
            * :code:`StaticArray[Byte, N]`: copy the bytes from another StaticArray. The argument's element type and length must exactly match Byte and this StaticBytes' length, otherwise an error will occur.
            * :code:`ComputedValue[StaticArray[Byte, N]]`: copy the bytes from a StaticArray produced by a ComputedType. The argument's element type and length must exactly match Byte and this StaticBytes' length, otherwise an error will occur.

        Args:
            values: The new elements this StaticBytes should have. This must follow the above constraints.

        Returns:
            An expression which stores the given value into this StaticBytes.
        """
        match values:
            case bytes() | bytearray():
                if len(values) != self.type_spec().length_static():
                    raise TealInputError(
                        f"Got bytes with length {len(values)}, expect {self.type_spec().length_static()}"
                    )
                return self.stored_value.store(Bytes(values))
            case Expr():
                return Seq(
                    self.stored_value.store(values),
                    Assert(self.length() == Len(self.stored_value.load())),
                )

        return super().set(values)
コード例 #5
0
def uint_encode(size: int, uint_var: Expr | ScratchVar) -> Expr:

    if isinstance(uint_var, ScratchVar):
        uint_var = uint_var.load()

    if size > 64:
        raise NotImplementedError(
            "Uint operations have not yet been implemented for bit sizes larger than 64"
        )

    if size == 8:
        return SetByte(Bytes(b"\x00"), Int(0), uint_var)
    if size == 16:
        return Suffix(Itob(uint_var), Int(6))
    if size == 32:
        return Suffix(Itob(uint_var), Int(4))
    if size == 64:
        return Itob(uint_var)

    raise ValueError("Unsupported uint size: {}".format(size))
コード例 #6
0
ファイル: string.py プロジェクト: algorand/pyteal
def _encoded_byte_string(s: bytes | bytearray) -> Expr:
    prefix = ABIType.from_string("uint16").encode(len(s))
    return Bytes(prefix + s)
コード例 #7
0
ファイル: bool.py プロジェクト: algorand/pyteal
 def encode(self) -> Expr:
     return SetBit(Bytes(b"\x00"), Int(0), self.get())
コード例 #8
0
ファイル: itxn.py プロジェクト: algorand/pyteal
    def MethodCall(
        cls,
        *,
        app_id: Expr,
        method_signature: str,
        args: list[abi.BaseType | Expr | dict[TxnField, Expr | list[Expr]]],
        extra_fields: dict[TxnField, Expr | list[Expr]] = None,
    ) -> Expr:
        """Adds an ABI method call transaction to the current inner transaction group.

        :any:`InnerTxnBuilder.Begin` must be called before a MethodCall can be added.

        Requires Teal version 6 or higher. This operation is only permitted in application mode.

        Args:
            app_id: An expression that evaluates to a `TealType.uint64` corresponding to the application being called.
            method_signature: A string representing the method signature of the method we're calling. This is used to do
                type checking on the arguments passed and to create the method selector passed as the first argument.
            args: A list of arguments to pass to the application. The values in this list depend on the kind of argument you wish to pass:

                - For basic ABI arguments (not Reference or Transaction types):
                    If an ABI type is passed it **MUST** match the type specified in the `method_signature`. If an Expr is passed it must evaluate to `TealType.bytes` but beyond that no type checking is performed.

                - For Reference arguments:
                    Either the Reference type or an Expr that returns the type corresponding to the reference type are allowed.
                    (i.e. Asset is TealType.uint64, Application is TealType.uint64, Account is TealType.bytes)

                - For Transaction arguments:
                    A dictionary containing TxnField to Expr that describe Transactions to be pre-pended to the transaction group being constructed.  The `TxnField.type_enum` key MUST be set and MUST match the expected transaction type specified in the `method_signature`.

            extra_fields (optional): A dictionary whose keys are fields to set and whose values are the value each
                field should take. Each value must evaluate to a type that is compatible with the
                field being set. These fields are set on the ApplicationCallTransaction being constructed
        """

        require_type(app_id, TealType.uint64)

        # Default, always need these
        fields_to_set = [
            cls.SetField(TxnField.type_enum, TxnType.ApplicationCall),
            cls.SetField(TxnField.application_id, app_id),
        ]

        # We only care about the args
        arg_type_specs: list[abi.TypeSpec]
        arg_type_specs, _ = abi.type_specs_from_signature(method_signature)

        if len(args) != len(arg_type_specs):
            raise TealInputError(
                f"Expected {len(arg_type_specs)} arguments, got {len(args)}"
            )

        # Start app args with the method selector
        app_args: list[Expr] = [MethodSignature(method_signature)]

        # Transactions are not included in the App Call
        txns_to_pass: list[Expr] = []

        # Reference Types are treated specially
        accts: list[Expr] = []
        apps: list[Expr] = []
        assets: list[Expr] = []

        for idx, method_arg_ts in enumerate(arg_type_specs):
            arg = args[idx]

            if method_arg_ts in abi.TransactionTypeSpecs:
                if not isinstance(arg, dict):
                    raise TealTypeError(arg, dict[TxnField, Expr | list[Expr]])

                if TxnField.type_enum not in arg:
                    raise TealInputError(
                        f"Expected Transaction at arg {idx} to contain field type_enum"
                    )

                if type(arg[TxnField.type_enum]) is not EnumInt:
                    raise TealTypeError(arg[TxnField.type_enum], EnumInt)

                txntype = cast(EnumInt, arg[TxnField.type_enum]).name
                # If the arg is an unspecified transaction, no need to check the type_enum
                if not type(
                    method_arg_ts
                ) is abi.TransactionTypeSpec and txntype != str(method_arg_ts):
                    raise TealInputError(
                        f"Expected Transaction at arg {idx} to be {method_arg_ts}, got {txntype}"
                    )

                txns_to_pass.append(InnerTxnBuilder.SetFields(arg))

            elif method_arg_ts in abi.ReferenceTypeSpecs:
                match method_arg_ts:
                    # For both acct and application, add index to
                    # app args _after_ appending since 0 is implicitly set
                    case abi.AccountTypeSpec():
                        if isinstance(arg, Expr):
                            # require the address is passed
                            require_type(arg, TealType.bytes)
                            accts.append(arg)
                        elif isinstance(arg, abi.Account):
                            accts.append(arg.address())
                        else:
                            raise TealTypeError(arg, abi.Account | Expr)

                        app_args.append(
                            Bytes(
                                algosdk.abi.ABIType.from_string("uint8").encode(
                                    len(accts)
                                )
                            )
                        )

                    case abi.ApplicationTypeSpec():
                        if isinstance(arg, Expr):
                            # require the app id be passed
                            require_type(arg, TealType.uint64)
                            apps.append(arg)
                        elif isinstance(arg, abi.Application):
                            apps.append(arg.application_id())
                        else:
                            raise TealTypeError(arg, abi.Application | Expr)

                        app_args.append(
                            Bytes(
                                algosdk.abi.ABIType.from_string("uint8").encode(
                                    len(apps)
                                )
                            )
                        )

                    # For assets, add to app_args prior to appending to assets array
                    case abi.AssetTypeSpec():
                        app_args.append(
                            Bytes(
                                algosdk.abi.ABIType.from_string("uint8").encode(
                                    len(assets)
                                )
                            )
                        )

                        if isinstance(arg, Expr):
                            require_type(arg, TealType.uint64)
                            assets.append(arg)
                        elif isinstance(arg, abi.Asset):
                            assets.append(arg.asset_id())
                        else:
                            raise TealTypeError(arg, abi.Asset | Expr)
            else:
                if isinstance(arg, Expr):
                    # This should _always_ be bytes, since we assume its already abi encoded
                    require_type(arg, TealType.bytes)
                    app_args.append(arg)
                elif isinstance(arg, abi.BaseType):
                    if arg.type_spec() != method_arg_ts:
                        raise TealTypeError(arg.type_spec(), method_arg_ts)
                    app_args.append(arg.encode())
                else:
                    raise TealTypeError(arg, abi.BaseType | Expr)

        if len(accts) > 0:
            fields_to_set.append(cls.SetField(TxnField.accounts, accts))

        if len(apps) > 0:
            fields_to_set.append(cls.SetField(TxnField.applications, apps))

        if len(assets) > 0:
            fields_to_set.append(cls.SetField(TxnField.assets, assets))

        fields_to_set.append(cls.SetField(TxnField.application_args, app_args))

        return Seq(
            # Add the transactions first
            *[Seq(ttp, InnerTxnBuilder.Next()) for ttp in txns_to_pass],
            # Set the fields for the app call in app args and foreign arrays
            *fields_to_set,
            # Add any remaining fields specified by the user
            InnerTxnBuilder.SetFields({} if extra_fields is None else extra_fields),
        )
コード例 #9
0
class OpUpMode(Enum):
    """An Enum object that defines the mode used for the OpUp utility.

    Note: the Explicit mode requires the app id to be provided
    through the foreign apps array in order for it to be accessible
    during evaluation.
    """

    # The app to call must be provided by the user.
    Explicit = 0

    # The app to call is created then deleted for each request to increase budget.
    OnCall = 1


ON_CALL_APP = Bytes("base16", "068101")  # v6 program "int 1"
MIN_TXN_FEE = Int(1000)


class OpUp:
    """Utility for increasing opcode budget during app execution.

    Requires program version 6 or higher.

    Example:
        .. code-block:: python

            # OnCall mode: doesn't accept target_app_id as an argument
            opup = OpUp(OpUpMode.OnCall)
            program_with_opup = Seq(
                ...,
コード例 #10
0
ファイル: address.py プロジェクト: algorand/pyteal
    def set(
        self,
        value: Union[
            str,
            bytes,
            Expr,
            Sequence[Byte],
            StaticArray[Byte, Literal[AddressLength.Bytes]],
            ComputedValue[StaticArray[Byte, Literal[AddressLength.Bytes]]],
            "Address",
            ComputedValue["Address"],
        ],
    ):
        """Set the value of this Address to the input value.

        The behavior of this method depends on the input argument type:

            * :code:`str`: set the value to the address from the encoded address string. This string must be a valid 58-character base-32 Algorand address with checksum.
            * :code:`bytes`: set the value to the raw address bytes. This byte string must have length 32.
            * :code:`Expr`: set the value to the result of a PyTeal expression, which must evaluate to a TealType.bytes. The program will fail if the evaluated byte string length is not 32.
            * :code:`Sequence[Byte]`: set the bytes of this Address to those contained in this Python sequence (e.g. a list or tuple). A compiler error will occur if the sequence length is not 32.
            * :code:`StaticArray[Byte, 32]`: copy the bytes from a StaticArray of 32 bytes.
            * :code:`ComputedValue[StaticArray[Byte, 32]]`: copy the bytes from a StaticArray of 32 bytes produced by a ComputedValue.
            * :code:`Address`: copy the value from another Address.
            * :code:`ComputedValue[Address]`: copy the value from an Address produced by a ComputedValue.

        Args:
            value: The new value this Address should take. This must follow the above constraints.

        Returns:
            An expression which stores the given value into this Address.
        """

        match value:
            case ComputedValue():
                pts = value.produced_type_spec()
                if pts == AddressTypeSpec() or pts == StaticArrayTypeSpec(
                    ByteTypeSpec(), AddressLength.Bytes
                ):
                    return value.store_into(self)

                raise TealInputError(
                    f"Got ComputedValue with type spec {pts}, expected AddressTypeSpec or StaticArray[Byte, Literal[AddressLength.Bytes]]"
                )
            case BaseType():
                if (
                    value.type_spec() == AddressTypeSpec()
                    or value.type_spec()
                    == StaticArrayTypeSpec(ByteTypeSpec(), AddressLength.Bytes)
                ):
                    return self.stored_value.store(value.stored_value.load())

                raise TealInputError(
                    f"Got {value} with type spec {value.type_spec()}, expected AddressTypeSpec"
                )
            case str():
                # Addr throws if value is invalid address
                return self.stored_value.store(Addr(value))
            case bytes():
                if len(value) == AddressLength.Bytes:
                    return self.stored_value.store(Bytes(value))
                raise TealInputError(
                    f"Got bytes with length {len(value)}, expected {AddressLength.Bytes}"
                )
            case Expr():
                return Seq(
                    self.stored_value.store(value),
                    Assert(
                        Len(self.stored_value.load()) == Int(AddressLength.Bytes.value)
                    ),
                )
            case CollectionSequence():
                if len(value) != AddressLength.Bytes:
                    raise TealInputError(
                        f"Got bytes with length {len(value)}, expected {AddressLength.Bytes}"
                    )
                return super().set(cast(Sequence[Byte], value))

        raise TealInputError(
            f"Got {type(value)}, expected Sequence, StaticArray, ComputedValue, Address, str, bytes, Expr"
        )
コード例 #11
0
ファイル: tuple.py プロジェクト: algorand/pyteal
def _encode_tuple(values: Sequence[BaseType]) -> Expr:
    heads: List[Expr] = []
    head_length_static: int = 0

    dynamicValueIndexToHeadIndex: Dict[int, int] = dict()
    ignoreNext = 0
    for i, elem in enumerate(values):
        if ignoreNext > 0:
            ignoreNext -= 1
            continue

        elemType = elem.type_spec()

        if elemType == BoolTypeSpec():
            numBools = _consecutive_bool_instance_num(values, i)
            ignoreNext = numBools - 1
            head_length_static += _bool_sequence_length(numBools)
            heads.append(
                _encode_bool_sequence(cast(Sequence[Bool], values[i : i + numBools]))
            )
            continue

        if elemType.is_dynamic():
            head_length_static += 2
            dynamicValueIndexToHeadIndex[i] = len(heads)
            heads.append(Seq())  # a placeholder
            continue

        head_length_static += elemType.byte_length_static()
        heads.append(elem.encode())

    tail_offset = Uint16()
    tail_offset_accumulator = Uint16()
    tail_holder = ScratchVar(TealType.bytes)
    encoded_tail = ScratchVar(TealType.bytes)

    firstDynamicTail = True
    for i, elem in enumerate(values):
        if elem.type_spec().is_dynamic():
            if firstDynamicTail:
                firstDynamicTail = False
                updateVars = Seq(
                    tail_holder.store(encoded_tail.load()),
                    tail_offset.set(head_length_static),
                )
            else:
                updateVars = Seq(
                    tail_holder.store(Concat(tail_holder.load(), encoded_tail.load())),
                    tail_offset.set(tail_offset_accumulator),
                )

            notLastDynamicValue = any(
                [nextValue.type_spec().is_dynamic() for nextValue in values[i + 1 :]]
            )
            if notLastDynamicValue:
                updateAccumulator = tail_offset_accumulator.set(
                    tail_offset.get() + Len(encoded_tail.load())
                )
            else:
                updateAccumulator = Seq()

            heads[dynamicValueIndexToHeadIndex[i]] = Seq(
                encoded_tail.store(elem.encode()),
                updateVars,
                updateAccumulator,
                tail_offset.encode(),
            )

    toConcat = heads
    if not firstDynamicTail:
        toConcat.append(tail_holder.load())

    if len(toConcat) == 0:
        return Bytes("")

    return Concat(*toConcat)