tronpytool.common.validation API documentation

# Module `tronpytool.common.validation`

Expand source code

    # --------------------------------------------------------------------------------------------
    # Copyright (c) iEXBase. All rights reserved.
    # Licensed under the MIT License.
    # See License.txt in the project root for license information.
    # --------------------------------------------------------------------------------------------
    import itertools
    import re

    from eth_utils import (
        function_abi_to_4byte_selector,
        apply_formatter_to_array
    )

    from trx_utils import (
        encode_hex,
        is_0x_prefixed,
        is_list_like,
        is_dict,
        is_string,
        is_bytes,
        is_boolean,
        is_integer,
        is_binary_address, is_hex_address, is_checksum_address)

    from tronpytool.common.account import PrivateKey
    from tronpytool.common.abi import filter_by_type, abi_to_signature, is_recognized_type, is_string_type, is_bytes_type, \
        is_address_type, is_int_type, is_uint_type, is_bool_type, sub_type_of_array_type, is_array_type, \
        length_of_array_type

    from tronpytool.common.toolz import (
        compose,
        groupby,
        valfilter,
        valmap,
    )
    from tronpytool.exceptions import InvalidAddress

    def _prepare_selector_collision_msg(duplicates):
        dup_sel = valmap(apply_formatter_to_array(abi_to_signature), duplicates)
        joined_funcs = valmap(lambda f: ', '.join(f), dup_sel)
        func_sel_msg_list = [funcs + ' have selector ' + sel for sel, funcs in joined_funcs.items()]
        return ' and\n'.join(func_sel_msg_list)

    def is_valid_url(value):
        """Return whether or not given value is a valid URL.

        Args:
            value(str): URL address string to validate
        """
        regex = re.compile(
            r'^(?:http|ftp)s?://'
            r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|'
            r'localhost|'
            r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|'
            r'\[?[A-F0-9]*:[A-F0-9:]+\]?)'
            r'(?::\d+)?'
            r'(?:/?|[/?]\S+)$', re.IGNORECASE)

        result = regex.match(value)
        return bool(result)

    def validate_abi(abi):
        """
        Helper function for validating an ABI
        """
        if not is_list_like(abi):
            raise ValueError("'abi' is not a list")

        if not all(is_dict(e) for e in abi):
            raise ValueError("'abi' is not a list of dictionaries")

        functions = filter_by_type('function', abi)
        selectors = groupby(
            compose(encode_hex, function_abi_to_4byte_selector),
            functions
        )
        duplicates = valfilter(lambda funcs: len(funcs) > 1, selectors)
        if duplicates:
            raise ValueError(
                'Abi contains functions with colliding selectors. '
                'Functions {0}'.format(_prepare_selector_collision_msg(duplicates))
            )

    def validate_abi_type(abi_type):
        """
        Helper function for validating an abi_type
        """
        if not is_recognized_type(abi_type):
            raise ValueError("Unrecognized abi_type: {abi_type}".format(abi_type=abi_type))

    def validate_abi_value(abi_type, value):
        """
        Helper function for validating a value against the expected abi_type
        Note: abi_type 'bytes' must either be python3 'bytes' object or ''
        """
        if is_array_type(abi_type) and is_list_like(value):
            # validate length
            specified_length = length_of_array_type(abi_type)
            if specified_length is not None:
                if specified_length < 1:
                    raise TypeError(
                        "Invalid abi-type: {abi_type}. Length of fixed sized arrays"
                        "must be greater than 0."
                            .format(abi_type=abi_type)
                    )
                if specified_length != len(value):
                    raise TypeError(
                        "The following array length does not the length specified"
                        "by the abi-type, {abi_type}: {value}"
                            .format(abi_type=abi_type, value=value)
                    )

            # validate sub_types
            sub_type = sub_type_of_array_type(abi_type)
            for v in value:
                validate_abi_value(sub_type, v)
            return
        elif is_bool_type(abi_type) and is_boolean(value):
            return
        elif is_uint_type(abi_type) and is_integer(value) and value >= 0:
            return
        elif is_int_type(abi_type) and is_integer(value):
            return
        elif is_address_type(abi_type):
            validate_address(value)
            return
        elif is_bytes_type(abi_type):
            if is_bytes(value):
                return
            elif is_string(value):
                if is_0x_prefixed(value):
                    return
                else:
                    raise TypeError(
                        "ABI values of abi-type 'bytes' must be either"
                        "a python3 'bytes' object or an '0x' prefixed string."
                    )
        elif is_string_type(abi_type) and is_string(value):
            return

        raise TypeError(
            "The following abi value is not a '{abi_type}': {value}".format(abi_type=abi_type, value=value)
        )

    def validate_address(value):
        """
        Helper function for validating an address
        """
        if is_bytes(value):
            if not is_binary_address(value):
                raise InvalidAddress("Address must be 20 bytes when input type is bytes", value)
            return

        if not isinstance(value, str):
            raise TypeError('Address {} must be provided as a string'.format(value))
        if not is_hex_address(value):
            raise InvalidAddress("Address must be 20 bytes, as a hex string with a 0x prefix", value)
        if not is_checksum_address(value):
            if value == value.lower():
                raise InvalidAddress(
                    "Web3.py only accepts checksum addresses. "
                    "The software that gave you this non-checksum address should be considered unsafe, "
                    "please file it as a bug on their platform. "
                    "Try using an ENS name instead. Or, if you must accept lower safety, "
                    "use Web3.toChecksumAddress(lower_case_address).",
                    value,
                )
            else:
                raise InvalidAddress(
                    "Address has an invalid EIP-55 checksum. "
                    "After looking up the address from the original source, try again.",
                    value,
                )

    def has_one_val(*args, **kwargs):
        vals = itertools.chain(args, kwargs.values())
        not_nones = list(filter(lambda val: val is not None, vals))
        return len(not_nones) == 1

    def assert_one_val(*args, **kwargs):
        if not has_one_val(*args, **kwargs):
            raise TypeError(
                "Exactly one of the passed values can be specified. "
                "Instead, values were: %r, %r" % (args, kwargs)
            )

    def is_private_key(i: any) -> bool:
        return isinstance(i, PrivateKey)

## Functions

`def assert_one_val(*args, **kwargs)`
  Expand source code

      def assert_one_val(*args, **kwargs):
          if not has_one_val(*args, **kwargs):
              raise TypeError(
                  "Exactly one of the passed values can be specified. "
                  "Instead, values were: %r, %r" % (args, kwargs)
              )

`def has_one_val(*args, **kwargs)`
  Expand source code

      def has_one_val(*args, **kwargs):
          vals = itertools.chain(args, kwargs.values())
          not_nones = list(filter(lambda val: val is not None, vals))
          return len(not_nones) == 1

`def is_private_key(i: ) ‑> bool`
  Expand source code

      def is_private_key(i: any) -> bool:
          return isinstance(i, PrivateKey)

`def is_valid_url(value)`

  Return whether or not given value is a valid URL.

  ## Args

  value(str): URL address string to validate

  Expand source code

      def is_valid_url(value):
          """Return whether or not given value is a valid URL.

          Args:
              value(str): URL address string to validate
          """
          regex = re.compile(
              r'^(?:http|ftp)s?://'
              r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|'
              r'localhost|'
              r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|'
              r'\[?[A-F0-9]*:[A-F0-9:]+\]?)'
              r'(?::\d+)?'
              r'(?:/?|[/?]\S+)$', re.IGNORECASE)

          result = regex.match(value)
          return bool(result)

`def validate_abi(abi)`

  Helper function for validating an ABI

  Expand source code

      def validate_abi(abi):
          """
          Helper function for validating an ABI
          """
          if not is_list_like(abi):
              raise ValueError("'abi' is not a list")

          if not all(is_dict(e) for e in abi):
              raise ValueError("'abi' is not a list of dictionaries")

          functions = filter_by_type('function', abi)
          selectors = groupby(
              compose(encode_hex, function_abi_to_4byte_selector),
              functions
          )
          duplicates = valfilter(lambda funcs: len(funcs) > 1, selectors)
          if duplicates:
              raise ValueError(
                  'Abi contains functions with colliding selectors. '
                  'Functions {0}'.format(_prepare_selector_collision_msg(duplicates))
              )

`def validate_abi_type(abi_type)`

  Helper function for validating an abi_type

  Expand source code

      def validate_abi_type(abi_type):
          """
          Helper function for validating an abi_type
          """
          if not is_recognized_type(abi_type):
              raise ValueError("Unrecognized abi_type: {abi_type}".format(abi_type=abi_type))

`def validate_abi_value(abi_type, value)`

  Helper function for validating a value against the expected abi_type Note: abi_type 'bytes' must either be python3 'bytes' object or ''

  Expand source code

      def validate_abi_value(abi_type, value):
          """
          Helper function for validating a value against the expected abi_type
          Note: abi_type 'bytes' must either be python3 'bytes' object or ''
          """
          if is_array_type(abi_type) and is_list_like(value):
              # validate length
              specified_length = length_of_array_type(abi_type)
              if specified_length is not None:
                  if specified_length < 1:
                      raise TypeError(
                          "Invalid abi-type: {abi_type}. Length of fixed sized arrays"
                          "must be greater than 0."
                              .format(abi_type=abi_type)
                      )
                  if specified_length != len(value):
                      raise TypeError(
                          "The following array length does not the length specified"
                          "by the abi-type, {abi_type}: {value}"
                              .format(abi_type=abi_type, value=value)
                      )

              # validate sub_types
              sub_type = sub_type_of_array_type(abi_type)
              for v in value:
                  validate_abi_value(sub_type, v)
              return
          elif is_bool_type(abi_type) and is_boolean(value):
              return
          elif is_uint_type(abi_type) and is_integer(value) and value >= 0:
              return
          elif is_int_type(abi_type) and is_integer(value):
              return
          elif is_address_type(abi_type):
              validate_address(value)
              return
          elif is_bytes_type(abi_type):
              if is_bytes(value):
                  return
              elif is_string(value):
                  if is_0x_prefixed(value):
                      return
                  else:
                      raise TypeError(
                          "ABI values of abi-type 'bytes' must be either"
                          "a python3 'bytes' object or an '0x' prefixed string."
                      )
          elif is_string_type(abi_type) and is_string(value):
              return

          raise TypeError(
              "The following abi value is not a '{abi_type}': {value}".format(abi_type=abi_type, value=value)
          )

`def validate_address(value)`

  Helper function for validating an address

  Expand source code

      def validate_address(value):
          """
          Helper function for validating an address
          """
          if is_bytes(value):
              if not is_binary_address(value):
                  raise InvalidAddress("Address must be 20 bytes when input type is bytes", value)
              return

          if not isinstance(value, str):
              raise TypeError('Address {} must be provided as a string'.format(value))
          if not is_hex_address(value):
              raise InvalidAddress("Address must be 20 bytes, as a hex string with a 0x prefix", value)
          if not is_checksum_address(value):
              if value == value.lower():
                  raise InvalidAddress(
                      "Web3.py only accepts checksum addresses. "
                      "The software that gave you this non-checksum address should be considered unsafe, "
                      "please file it as a bug on their platform. "
                      "Try using an ENS name instead. Or, if you must accept lower safety, "
                      "use Web3.toChecksumAddress(lower_case_address).",
                      value,
                  )
              else:
                  raise InvalidAddress(
                      "Address has an invalid EIP-55 checksum. "
                      "After looking up the address from the original source, try again.",
                      value,
                  )