Exemple #1
0
    def invoke(
        self,
        args: list[Expr | ScratchVar | abi.BaseType],
    ) -> "SubroutineCall":
        if len(args) != self.argument_count():
            raise TealInputError(
                f"Incorrect number of arguments for subroutine call. "
                f"Expected {self.arguments()} arguments, got {len(args)} arguments"
            )

        for i, arg in enumerate(args):
            arg_type = self.expected_arg_types[i]
            if arg_type is Expr and not isinstance(arg, Expr):
                raise TealInputError(
                    f"supplied argument {arg} at index {i} had type {type(arg)} but was expecting type {arg_type}"
                )
            elif arg_type is ScratchVar and not isinstance(arg, ScratchVar):
                raise TealInputError(
                    f"supplied argument {arg} at index {i} had type {type(arg)} but was expecting type {arg_type}"
                )
            elif isinstance(arg_type, abi.TypeSpec):
                if not isinstance(arg, abi.BaseType):
                    raise TealInputError(
                        f"supplied argument at index {i} should be an ABI type but got {arg}"
                    )
                if arg.type_spec() != arg_type:
                    raise TealInputError(
                        f"supplied argument {arg} at index {i} "
                        f"should have ABI typespec {arg_type} but got {arg.type_spec()}"
                    )

        return SubroutineCall(
            self, args, output_kwarg=OutputKwArgInfo.from_dict(self.output_kwarg)
        )
Exemple #2
0
    def set(
        self,
        values: Union[
            Sequence[T], "StaticArray[T, N]", ComputedValue["StaticArray[T, N]"]
        ],
    ) -> Expr:
        """Set the elements of this StaticArray to the input values.

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

            * :code:`Sequence[T]`: set the elements of this StaticArray to those contained in this Python sequence (e.g. a list or tuple). A compiler error will occur if any element in the sequence does not match this StaticArray's element type, or if the sequence length does not equal this StaticArray's length.
            * :code:`StaticArray[T, N]`: copy the elements from another StaticArray. The argument's element type and length must exactly match this StaticArray's element type and length, otherwise an error will occur.
            * :code:`ComputedValue[StaticArray[T, N]]`: copy the elements from a StaticArray produced by a ComputedValue. The element type and length produced by the ComputedValue must exactly match this StaticArray's element type and length, otherwise an error will occur.

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

        Returns:
            An expression which stores the given value into this StaticArray.
        """
        if isinstance(values, ComputedValue):
            return self._set_with_computed_type(values)
        elif isinstance(values, BaseType):
            if self.type_spec() != values.type_spec():
                raise TealInputError(
                    f"Cannot assign type {values.type_spec()} to {self.type_spec()}"
                )
            return self.stored_value.store(values.encode())

        if self.type_spec().length_static() != len(values):
            raise TealInputError(
                f"Incorrect length for values. Expected {self.type_spec()}, got {len(values)}"
            )
        return super().set(values)
Exemple #3
0
    def _get_output_kwarg_info(
        cls, fn_implementation: Callable[..., Expr]
    ) -> Optional[OutputKwArgInfo]:
        if not callable(fn_implementation):
            raise TealInputError("Input to ABIReturnSubroutine is not callable")
        sig = signature(fn_implementation)
        fn_annotations = get_annotations(fn_implementation)

        potential_abi_arg_names = [
            k for k, v in sig.parameters.items() if v.kind == Parameter.KEYWORD_ONLY
        ]

        match potential_abi_arg_names:
            case []:
                return None
            case [name]:
                if name != cls.OUTPUT_ARG_NAME:
                    raise TealInputError(
                        f"ABI return subroutine output-kwarg name must be `output` at this moment, "
                        f"while {name} is the keyword."
                    )
                annotation = fn_annotations.get(name, None)
                if annotation is None:
                    raise TealInputError(
                        f"ABI return subroutine output-kwarg {name} must specify ABI type"
                    )
                type_spec = abi.type_spec_from_annotation(annotation)
                return OutputKwArgInfo(name, type_spec)
            case _:
                raise TealInputError(
                    f"multiple output arguments ({len(potential_abi_arg_names)}) "
                    f"with type annotations {potential_abi_arg_names}"
                )
Exemple #4
0
    def __init__(
        self,
        subroutine: SubroutineDefinition,
        args: list[Expr | ScratchVar | abi.BaseType],
        *,
        output_kwarg: OutputKwArgInfo = None,
    ) -> None:
        super().__init__()
        self.subroutine = subroutine
        self.args = args
        self.output_kwarg = output_kwarg

        for i, arg in enumerate(args):
            if isinstance(arg, Expr):
                arg_type = arg.type_of()
            elif isinstance(arg, ScratchVar):
                arg_type = arg.type
            elif isinstance(arg, abi.BaseType):
                arg_type = cast(abi.BaseType, arg).stored_value.type
            else:
                raise TealInputError(
                    f"Subroutine argument {arg} at index {i} was of unexpected Python type {type(arg)}"
                )

            if arg_type == TealType.none:
                raise TealInputError(
                    f"Subroutine argument {arg} at index {i} evaluates to TealType.none"
                )
Exemple #5
0
    def __init__(self, mode: OpUpMode, target_app_id: Expr = None):
        """Create a new OpUp object.

        Args:
            mode: OpUpMode that determines the style of budget increase
                to use. See the OpUpMode Enum for more information.
            target_app_id (optional): In Explicit mode, the OpUp utility
                requires the app_id to target for inner app calls. Defaults
                to None.
        """

        # With only OnCall and Explicit modes supported, the mode argument
        # isn't strictly necessary but it will most likely be required if
        # we do decide to add more modes in the future.
        if mode == OpUpMode.Explicit:
            if target_app_id is None:
                raise TealInputError(
                    "target_app_id must be specified in Explicit OpUp mode")
            require_type(target_app_id, TealType.uint64)
            self.target_app_id = target_app_id
        elif mode == OpUpMode.OnCall:
            if target_app_id is not None:
                raise TealInputError(
                    "target_app_id is not used in OnCall OpUp mode")
        else:
            raise TealInputError("Invalid OpUp mode provided")

        self.mode = mode
Exemple #6
0
 def _validate_annotation(
     user_defined_annotations: dict[str, Any], parameter_name: str
 ) -> type[Expr] | type[ScratchVar] | abi.TypeSpec:
     ptype = user_defined_annotations.get(parameter_name, None)
     if ptype is None:
         # Without a type annotation, `SubroutineDefinition` presumes an implicit `Expr` declaration
         # rather than these alternatives:
         # * Throw error requiring type annotation.
         # * Defer parameter type checks until arguments provided during invocation.
         #
         # * Rationale:
         #   * Provide an upfront, best-effort type check before invocation.
         #   * Preserve backwards compatibility with TEAL programs written
         #     when `Expr` is the only supported annotation type.
         # * `invoke` type checks provided arguments against parameter types to catch mismatches.
         return Expr
     if ptype in (Expr, ScratchVar):
         return ptype
     if SubroutineDefinition._is_abi_annotation(ptype):
         return abi.type_spec_from_annotation(ptype)
     if not isclass(ptype):
         raise TealInputError(
             f"Function has parameter {parameter_name} of declared type {ptype} which is not a class"
         )
     raise TealInputError(
         f"Function has parameter {parameter_name} of disallowed type {ptype}. "
         f"Only the types {(Expr, ScratchVar, 'ABI')} are allowed"
     )
Exemple #7
0
    def set(self, *values):
        """
        set(*values: BaseType) -> pyteal.Expr
        set(values: ComputedValue[Tuple]) -> pyteal.Expr

        Set the elements of this Tuple to the input values.

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

            * Variable number of :code:`BaseType` arguments: set the elements of this Tuple to the arguments to this method. A compiler error will occur if any argument does not match this Tuple's element type at the same index, or if the total argument count does not equal this Tuple's length.
            * :code:`ComputedValue[Tuple]`: copy the elements from a Tuple produced by a ComputedValue. The element types and length produced by the ComputedValue must exactly match this Tuple's element types and length, otherwise an error will occur.

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

        Returns:
            An expression which stores the given value into this Tuple.
        """
        if len(values) == 1 and isinstance(values[0], ComputedValue):
            return self._set_with_computed_type(values[0])

        for value in values:
            if not isinstance(value, BaseType):
                raise TealInputError(f"Expected BaseType, got {value}")

        myTypes = self.type_spec().value_type_specs()
        if len(myTypes) != len(values):
            raise TealInputError(
                f"Incorrect length for values. Expected {len(myTypes)}, got {len(values)}"
            )
        if not all(myTypes[i] == values[i].type_spec() for i in range(len(myTypes))):
            raise TealInputError("Input values do not match type")
        return self.stored_value.store(_encode_tuple(values))
Exemple #8
0
    def set(
        self,
        value: Union[
            str,
            bytes,
            Expr,
            Sequence[Byte],
            DynamicArray[Byte],
            ComputedValue[DynamicArray[Byte]],
            "String",
            ComputedValue["String"],
        ],
    ) -> Expr:
        """Set the value of this String to the input value.

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

            * :code:`str`: set the value to the Python string.
            * :code:`bytes`: set the value to the Python byte string.
            * :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:`DynamicArray[Byte]`: copy the bytes from a DynamicArray.
            * :code:`ComputedValue[DynamicArray[Byte]]`: copy the bytes from a DynamicArray produced by a ComputedValue.
            * :code:`String`: copy the value from another String.
            * :code:`ComputedValue[String]`: copy the value from a String produced by a ComputedValue.

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

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

        match value:
            case ComputedValue():
                return self._set_with_computed_type(value)
            case BaseType():
                if value.type_spec() == StringTypeSpec() or (
                    value.type_spec() == DynamicArrayTypeSpec(ByteTypeSpec())
                ):
                    return self.stored_value.store(value.stored_value.load())

                raise TealInputError(
                    f"Got {value} with type spec {value.type_spec()}, expected {StringTypeSpec}"
                )
            case bytes() | bytearray():
                return self.stored_value.store(_encoded_byte_string(value))
            case str():
                return self.stored_value.store(_encoded_byte_string(value.encode()))
            case Expr():
                return _store_encoded_expr_byte_string_into_var(
                    value, self.stored_value
                )
            case CollectionSequence():
                return super().set(cast(Sequence[Byte], value))

        raise TealInputError(
            f"Got {type(value)}, expected DynamicArray, ComputedValue, String, str, bytes, Expr"
        )
Exemple #9
0
    def add_method_handler(
        self,
        method_call: ABIReturnSubroutine,
        overriding_name: str = None,
        method_config: MethodConfig = None,
        description: str = None,
    ) -> ABIReturnSubroutine:
        """Add a method call handler to this Router.

        Args:
            method_call: An ABIReturnSubroutine that implements the method body.
            overriding_name (optional): A name for this method. Defaults to the function name of
                method_call.
            method_config (optional): An object describing the on completion actions and
                creation/non-creation call statuses that are valid for calling this method. All
                invalid configurations will be rejected. Defaults to :code:`MethodConfig(no_op=CallConfig.CALL)`
                (i.e. only the no-op action during a non-creation call is accepted) if none is provided.
            description (optional): A description for this method. Defaults to the docstring of
                method_call, if there is one.
        """
        if not isinstance(method_call, ABIReturnSubroutine):
            raise TealInputError(
                "for adding method handler, must be ABIReturnSubroutine"
            )
        method_signature = method_call.method_signature(overriding_name)
        if method_config is None:
            method_config = MethodConfig(no_op=CallConfig.CALL)
        if method_config.is_never():
            raise TealInputError(
                f"registered method {method_signature} is never executed"
            )
        method_selector = encoding.checksum(bytes(method_signature, "utf-8"))[:4]

        if method_signature in self.method_sig_to_selector:
            raise TealInputError(f"re-registering method {method_signature} detected")
        if method_selector in self.method_selector_to_sig:
            raise TealInputError(
                f"re-registering method {method_signature} has hash collision "
                f"with {self.method_selector_to_sig[method_selector]}"
            )

        meth = method_call.method_spec()
        if description is not None:
            meth.desc = description
        self.methods.append(meth)

        self.method_sig_to_selector[method_signature] = method_selector
        self.method_selector_to_sig[method_selector] = method_signature

        method_approval_cond = method_config.approval_cond()
        method_clear_state_cond = method_config.clear_state_cond()
        self.approval_ast.add_method_to_ast(
            method_signature, method_approval_cond, method_call
        )
        self.clear_state_ast.add_method_to_ast(
            method_signature, method_clear_state_cond, method_call
        )
        return method_call
Exemple #10
0
    def __init__(self, arg1: Union[str, bytes, bytearray], arg2: str = None) -> None:
        """
        __init__(arg1: Union[str, bytes, bytearray]) -> None
        __init__(self, arg1: str, arg2: str) -> None

        Create a new byte string.

        Depending on the encoding, there are different arguments to pass:

        For UTF-8 strings:
            Pass the string as the only argument. For example, ``Bytes("content")``.
        For raw bytes or bytearray objects:
            Pass the bytes or bytearray as the only argument. For example, ``Bytes(b"content")``.
        For base16, base32, or base64 strings:
            Pass the base as the first argument and the string as the second argument. For example,
            ``Bytes("base16", "636F6E74656E74")``, ``Bytes("base32", "ORFDPQ6ARJK")``,
            ``Bytes("base64", "Y29udGVudA==")``.
        Special case for base16:
            The prefix "0x" may be present in a base16 byte string. For example,
            ``Bytes("base16", "0x636F6E74656E74")``.
        """
        super().__init__()
        if arg2 is None:
            if type(arg1) is str:
                self.base = "utf8"
                self.byte_str = escapeStr(arg1)
            elif type(arg1) in (bytes, bytearray):
                self.base = "base16"
                self.byte_str = cast(Union[bytes, bytearray], arg1).hex()
            else:
                raise TealInputError("Unknown argument type: {}".format(type(arg1)))
        else:
            if type(arg1) is not str:
                raise TealInputError("Unknown type for base: {}".format(type(arg1)))

            if type(arg2) is not str:
                raise TealInputError("Unknown type for value: {}".format(type(arg2)))

            self.base = arg1

            if self.base == "base32":
                valid_base32(arg2)
                self.byte_str = arg2
            elif self.base == "base64":
                self.byte_str = arg2
                valid_base64(self.byte_str)
            elif self.base == "base16":
                if arg2.startswith("0x"):
                    self.byte_str = arg2[2:]
                else:
                    self.byte_str = arg2
                valid_base16(self.byte_str)
            else:
                raise TealInputError(
                    "invalid base {}, need to be base32, base64, or base16.".format(
                        self.base
                    )
                )
Exemple #11
0
    def SetField(cls, field: TxnField, value: Expr | list[Expr]) -> Expr:
        """Set a field of the current inner transaction.

        :any:`InnerTxnBuilder.Begin` must be called before setting any fields on an inner
        transaction.

        Note: For non-array field (e.g., note), setting it twice will overwrite the original value.
              While for array field (e.g., accounts), setting it multiple times will append the values.
              It is also possible to pass the entire array field if desired (e.g., Txn.accounts) to pass all the references.

        Requires program version 5 or higher. This operation is only permitted in application mode.

        Args:
            field: The field to set on the inner transaction.
            value: The value to that the field should take. This must evaluate to a type that is
                compatible with the field being set.
        """
        if not field.is_array:
            if type(value) is list or isinstance(value, TxnArray):
                raise TealInputError(
                    "inner transaction set field {} does not support array value".format(
                        field
                    )
                )
            return InnerTxnFieldExpr(field, cast(Expr, value))
        else:
            if type(value) is not list and not isinstance(value, TxnArray):
                raise TealInputError(
                    "inner transaction set array field {} with non-array value".format(
                        field
                    )
                )

            if type(value) is list:
                for valueIter in value:
                    if not isinstance(valueIter, Expr):
                        raise TealInputError(
                            "inner transaction set array field {} with non PyTeal expression array element {}".format(
                                field, valueIter
                            )
                        )

                return Seq(
                    *[
                        InnerTxnFieldExpr(field, cast(Expr, valueIter))
                        for valueIter in value
                    ]
                )
            else:
                arr = cast(TxnArray, value)
                return For(
                    (i := ScratchVar()).store(Int(0)),
                    i.load() < arr.length(),
                    i.store(i.load() + Int(1)),
                ).Do(InnerTxnFieldExpr(field, arr[i.load()]))
Exemple #12
0
    def ElseIf(self, cond):
        if not self.alternateSyntaxFlag:
            raise TealInputError("Cannot mix two different If syntax styles")

        if not self.elseBranch:
            self.elseBranch = If(cond)
        else:
            if not isinstance(self.elseBranch, If):
                raise TealInputError("Else-ElseIf block is malformed")
            self.elseBranch.ElseIf(cond)
        return self
Exemple #13
0
def valid_address(address: str):
    """check if address is a valid address with checksum"""
    if type(address) is not str:
        raise TealInputError("An address needs to be a string")

    if len(address) != 58:
        raise TealInputError(
            "Address length is not correct. Should " +
            "be a base 32 string encoded 32 bytes public key + 4 bytes checksum"
        )

    valid_base32(address)
Exemple #14
0
 def clear_state_condition_under_config(self) -> int:
     match self:
         case CallConfig.NEVER:
             return 0
         case CallConfig.CALL:
             return 1
         case CallConfig.CREATE | CallConfig.ALL:
             raise TealInputError(
                 "Only CallConfig.CALL or CallConfig.NEVER are valid for a clear state CallConfig, since clear state can never be invoked during creation"
             )
         case _:
             raise TealInputError(f"unexpected CallConfig {self}")
Exemple #15
0
    def __init__(self, methodName: str) -> None:
        """Create a new method selector for ABI method call.

        Args:
            methodName: A string containing a valid ABI method signature
        """
        super().__init__()
        if type(methodName) is not str:
            raise TealInputError("invalid input type {} to Method".format(
                type(methodName)))
        elif len(methodName) == 0:
            raise TealInputError("invalid input empty string to Method")
        self.methodName = methodName
Exemple #16
0
    def Then(self, thenBranch: Expr, *then_branch_multi: Expr):
        if not self.alternateSyntaxFlag:
            raise TealInputError("Cannot mix two different If syntax styles")

        thenBranch = _use_seq_if_multiple(thenBranch, *then_branch_multi)

        if not self.elseBranch:
            self.thenBranch = thenBranch
        else:
            if not isinstance(self.elseBranch, If):
                raise TealInputError("Else-Then block is malformed")
            self.elseBranch.Then(thenBranch)
        return self
Exemple #17
0
 def __init__(self, op: Op, inputType: TealType, outputType: TealType,
              args: Sequence[Expr]):
     super().__init__()
     if len(args) == 0:
         raise TealInputError("NaryExpr requires at least one child")
     for arg in args:
         if not isinstance(arg, Expr):
             raise TealInputError(
                 "Argument is not a PyTeal expression: {}".format(arg))
         require_type(arg, inputType)
     self.op = op
     self.outputType = outputType
     self.args = args
Exemple #18
0
def algosdk_from_type_spec(t: TypeSpec) -> algosdk.abi.ABIType:
    from pyteal.ast.abi import ReferenceTypeSpecs, TransactionTypeSpecs

    if t in TransactionTypeSpecs:
        raise TealInputError(
            f"cannot map ABI transaction type spec {t!r} to an appropriate algosdk ABI type"
        )

    if t in ReferenceTypeSpecs:
        raise TealInputError(
            f"cannot map ABI reference type spec {t!r} to an appropriate algosdk ABI type"
        )

    return algosdk.abi.ABIType.from_string(str(t))
Exemple #19
0
    def __getitem__(self, txnIndex: int) -> TxnObject:
        if type(txnIndex) is not int:
            raise TealInputError(
                "Invalid gitxn syntax, immediate txn index must be int.")

        if txnIndex < 0 or txnIndex >= MAX_GROUP_SIZE:
            raise TealInputError(
                "Invalid Gtxn index {}, should be in [0, {})".format(
                    txnIndex, MAX_GROUP_SIZE))

        return TxnObject(
            lambda field: GitxnExpr(txnIndex, field),
            lambda field, index: GitxnaExpr(txnIndex, field, index),
        )
Exemple #20
0
 def __init__(self, op: Op, name: str, field: TxnField) -> None:
     super().__init__()
     if field.is_array:
         raise TealInputError("Unexpected array field: {}".format(field))
     self.op = op
     self.name = name
     self.field = field
Exemple #21
0
 def __call__(self, *args: Expr | ScratchVar | abi.BaseType, **kwargs: Any) -> Expr:
     if len(kwargs) != 0:
         raise TealInputError(
             f"Subroutine cannot be called with keyword arguments. "
             f"Received keyword arguments: {','.join(kwargs.keys())}"
         )
     return self.subroutine.invoke(list(args))
Exemple #22
0
def EcdsaRecover(curve: EcdsaCurve, data: Expr, recovery_id: Expr, sigA: Expr,
                 sigB: Expr) -> MultiValue:
    """Recover an ECDSA public key from a signature.
    All byte arguments must be big endian encoded.
    Args:
        curve: Enum representing the ECDSA curve used for the public key
        data: Hash value of the signed data. Must be 32 bytes long.
        recovery_id: value used to extract public key from signature. Must evaluate to uint.
        sigA: First component of the signature. Must evaluate to bytes.
        sigB: Second component of the signature. Must evaluate to bytes.
    Returns:
        A MultiValue expression representing the two components of the public key, big endian
        encoded.
    """

    if not isinstance(curve, EcdsaCurve):
        raise TealTypeError(curve, EcdsaCurve)

    if curve != EcdsaCurve.Secp256k1:
        raise TealInputError("Recover only supports Secp256k1")

    require_type(data, TealType.bytes)
    require_type(recovery_id, TealType.uint64)
    require_type(sigA, TealType.bytes)
    require_type(sigB, TealType.bytes)
    return MultiValue(
        Op.ecdsa_pk_recover,
        EcdsaPubkey,
        immediate_args=[curve.arg_name],
        args=[data, recovery_id, sigA, sigB],
        compile_check=lambda options: verifyFieldVersion(
            curve.arg_name, curve.min_version, options.version),
    )
Exemple #23
0
    def __init__(
        self, slot: Optional[ScratchSlot], value: Expr, index_expression: Expr = None
    ):
        """Create a new ScratchStore expression.

        Args:
            slot (optional): The slot to store the value in.
            value: The value to store.
            index_expression (optional): As an alternative to slot,
                an expression can be supplied for the slot index.
        """
        super().__init__()

        if (slot is None) == (index_expression is None):
            raise TealInternalError(
                "Exactly one of slot or index_expressions must be provided"
            )

        if index_expression:
            if not isinstance(index_expression, Expr):
                raise TealInputError(
                    "index_expression must be an Expr but was of type {}".format(
                        type(index_expression)
                    )
                )
            require_type(index_expression, TealType.uint64)

        self.slot = slot
        self.value = value
        self.index_expression = index_expression
Exemple #24
0
 def __init__(self, single_line_comment: str) -> None:
     super().__init__()
     if "\n" in single_line_comment or "\r" in single_line_comment:
         raise TealInputError(
             "Newlines should not be present in the CommentExpr constructor"
         )
     self.comment = single_line_comment
Exemple #25
0
def validate_txn_index_or_throw(txnIndex: Union[int, Expr]):
    if not isinstance(txnIndex, (int, Expr)):
        raise TealInputError(
            f"Invalid txnIndex type:  Expected int or Expr, but received {txnIndex}"
        )
    if isinstance(txnIndex, Expr):
        require_type(txnIndex, TealType.uint64)
Exemple #26
0
    def __init__(self, value: int) -> None:
        """Create a new uint64.

        Args:
            value: The integer value this uint64 will represent. Must be a positive value less than
                2**64.
        """
        super().__init__()

        if type(value) is not int:
            raise TealInputError("invalid input type {} to Int".format(
                type(value)))
        elif value >= 0 and value < 2**64:
            self.value = value
        else:
            raise TealInputError("Int {} is out of range".format(value))
Exemple #27
0
    def set(self, value: Union[int, Expr, "Uint", ComputedValue["Uint"]]) -> Expr:
        """Set the value of this Uint to the input value.

        There are a variety of ways to express the input value. Regardless of the type used to
        indicate the input value, this Uint type can only hold values in the range :code:`[0,2^N)`,
        where :code:`N` is the bit size of this Uint.

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

            * :code:`int`: set the value to a Python integer. A compiler error will occur if this value overflows or underflows this integer type.
            * :code:`Expr`: set the value to the result of a PyTeal expression, which must evaluate to a TealType.uint64. The program will fail if the evaluated value overflows or underflows this integer type.
            * :code:`Uint`: copy the value from another Uint. The argument's type must exactly match this integer's type, otherwise an error will occur. For example, it's not possible to set a Uint64 to a Uint8, or vice versa.
            * :code:`ComputedValue[Uint]`: copy the value from a Uint produced by a ComputedValue. The type produced by the ComputedValue must exactly match this integer's type, otherwise an error will occur.

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

        Returns:
            An expression which stores the given value into this Uint.
        """
        if isinstance(value, ComputedValue):
            return self._set_with_computed_type(value)

        if isinstance(value, BaseType) and not (
            isinstance(value.type_spec(), UintTypeSpec)
            and self.type_spec().bit_size()
            == cast(UintTypeSpec, value.type_spec()).bit_size()
        ):
            raise TealInputError(
                "Type {} is not assignable to type {}".format(
                    value.type_spec(), self.type_spec()
                )
            )
        return uint_set(self.type_spec().bit_size(), self.stored_value, value)
Exemple #28
0
def valid_base16(s: str):
    """check if s is a valid hex encoding string"""
    pattern = re.compile(r"[0-9A-Fa-f]*")

    if pattern.fullmatch(s) is None:
        raise TealInputError(
            "{} is not a valid RFC 4648 base 16 string".format(s))
Exemple #29
0
def substring_for_decoding(
    encoded: Expr,
    *,
    start_index: Expr = None,
    end_index: Expr = None,
    length: Expr = None,
) -> Expr:
    """A helper function for getting the substring to decode according to the rules of BaseType.decode."""
    if length is not None and end_index is not None:
        raise TealInputError("length and end_index are mutually exclusive arguments")

    if start_index is not None:
        if length is not None:
            # substring from start_index to start_index + length
            return Extract(encoded, start_index, length)

        if end_index is not None:
            # substring from start_index to end_index
            return Substring(encoded, start_index, end_index)

        # substring from start_index to end of string
        return Suffix(encoded, start_index)

    if length is not None:
        # substring from 0 to length
        return Extract(encoded, Int(0), length)

    if end_index is not None:
        # substring from 0 to end_index
        return Substring(encoded, Int(0), end_index)

    # the entire string
    return encoded
Exemple #30
0
def valid_base64(s: str):
    """check if s is a valid base64 encoding string"""
    pattern = re.compile(
        r"^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$")

    if pattern.fullmatch(s) is None:
        raise TealInputError(
            "{} is not a valid RFC 4648 base 64 string".format(s))