Beispiel #1
0
def generate_cache_key(value):
    """
    Generates a cache key for the *args and **kwargs
    """
    if is_bytes(value):
        return hashlib.md5(value).hexdigest()
    elif is_text(value):
        return generate_cache_key(force_bytes(value))
    elif is_boolean(value) or is_null(value) or is_number(value):
        return generate_cache_key(repr(value))
    elif is_dict(value):
        return generate_cache_key((
            (key, value[key])
            for key
            in sorted(value.keys())
        ))
    elif is_list_like(value) or isinstance(value, Generator):
        return generate_cache_key("".join((
            generate_cache_key(item)
            for item
            in value
        )))
    else:
        raise TypeError("Cannot generate cache key for value {0} of type {1}".format(
            value,
            type(value),
        ))
Beispiel #2
0
def find_matching_fn_abi(abi, fn_identifier=None, args=None, kwargs=None):
    filters = []

    if fn_identifier:
        if fn_identifier is FallbackFn:
            return get_fallback_func_abi(abi)
        elif is_text(fn_identifier):
            filters.append(functools.partial(filter_by_name, fn_identifier))
        else:
            raise TypeError("Unsupported function identifier")

    if args is not None or kwargs is not None:
        if args is None:
            args = tuple()
        if kwargs is None:
            kwargs = {}

        num_arguments = len(args) + len(kwargs)
        filters.extend([
            functools.partial(filter_by_argument_count, num_arguments),
            functools.partial(filter_by_encodability, args, kwargs),
        ])

    function_candidates = pipe(abi, *filters)

    if len(function_candidates) == 1:
        return function_candidates[0]
    if not function_candidates:
        raise ValueError("No matching functions found")
    else:
        raise ValueError("Multiple functions found")
def test_encode_text_string(string_value):
    encoder = TextStringEncoder()

    if not is_text(string_value):
        with pytest.raises(EncodingTypeError) as exception_info:
            encoder(string_value)
        assert 'TextStringEncoder' in str(exception_info.value)
        return

    string_value_as_bytes = codecs.encode(string_value, 'utf8')

    expected_value = (
        encode_uint_256(len(string_value_as_bytes)) +
        (
            zpad_right(
                string_value_as_bytes,
                ceil32(len(string_value_as_bytes)),
            )
            if string_value
            else b'\x00' * 32
        )
    )
    encoded_value = encoder(string_value)

    assert encoded_value == expected_value
Beispiel #4
0
def encode_transaction_data(abi, web3, fn_identifier, args=None, kwargs=None):
    if fn_identifier is FallbackFn:
        fn_abi, fn_selector, fn_arguments = get_fallback_function_info(abi)
    elif is_text(fn_identifier):
        fn_abi, fn_selector, fn_arguments = get_function_info(
            abi, fn_identifier, args, kwargs
        )
    else:
        raise TypeError("Unsupported function identifier")

    return add_0x_prefix(encode_abi(web3, fn_abi, fn_arguments, fn_selector))
Beispiel #5
0
def find_matching_fn_abi(abi, fn_identifier=None, args=None, kwargs=None):
    args = args or tuple()
    kwargs = kwargs or dict()
    filters = []
    num_arguments = len(args) + len(kwargs)

    if fn_identifier is FallbackFn:
        return get_fallback_func_abi(abi)

    if not is_text(fn_identifier):
        raise TypeError("Unsupported function identifier")

    name_filter = functools.partial(filter_by_name, fn_identifier)
    arg_count_filter = functools.partial(filter_by_argument_count, num_arguments)
    encoding_filter = functools.partial(filter_by_encodability, args, kwargs)
    filters.extend([
        name_filter,
        arg_count_filter,
        encoding_filter,
    ])
    function_candidates = pipe(abi, *filters)
    if len(function_candidates) == 1:
        return function_candidates[0]
    else:
        matching_identifiers = name_filter(abi)
        matching_function_signatures = [abi_to_signature(func) for func in matching_identifiers]
        arg_count_matches = len(arg_count_filter(matching_identifiers))
        encoding_matches = len(encoding_filter(matching_identifiers))
        if arg_count_matches == 0:
            diagnosis = "\nFunction invocation failed due to improper number of arguments."
        elif encoding_matches == 0:
            diagnosis = "\nFunction invocation failed due to no matching argument types."
        elif encoding_matches > 1:
            diagnosis = (
                "\nAmbiguous argument encoding. "
                "Provided arguments can be encoded to multiple functions matching this call."
            )
        message = (
            "\nCould not identify the intended function with name `{name}`, "
            "positional argument(s) of type `{arg_types}` and "
            "keyword argument(s) of type `{kwarg_types}`."
            "\nFound {num_candidates} function(s) with the name `{name}`: {candidates}"
            "{diagnosis}"
        ).format(
            name=fn_identifier,
            arg_types=tuple(map(type, args)),
            kwarg_types=valmap(type, kwargs),
            num_candidates=len(matching_identifiers),
            candidates=matching_function_signatures,
            diagnosis=diagnosis,
        )
        raise ValidationError(message)
Beispiel #6
0
    def _set_function_info(self):
        self.abi = find_matching_fn_abi(self.contract_abi,
                                        self.function_identifier,
                                        self.args,
                                        self.kwargs)

        if self.function_identifier is FallbackFn:
            self.selector = encode_hex(b'')
        elif is_text(self.function_identifier):
            self.selector = encode_hex(function_abi_to_4byte_selector(self.abi))
        else:
            raise TypeError("Unsupported function identifier")

        self.arguments = merge_args_and_kwargs(self.abi, self.args, self.kwargs)
Beispiel #7
0
def is_predefined_block_number(value):
    if is_text(value):
        value_text = value
    elif is_bytes(value):
        # `value` could either be random bytes or the utf-8 encoding of
        # one of the words in: {"latest", "pending", "earliest"}
        # We cannot decode the bytes as utf8, because random bytes likely won't be valid.
        # So we speculatively decode as 'latin-1', which cannot fail.
        value_text = value.decode('latin-1')
    elif is_integer(value):
        return False
    else:
        raise TypeError("unrecognized block reference: %r" % value)

    return value_text in {"latest", "pending", "earliest"}
Beispiel #8
0
def find_matching_fn_abi(
    abi: ABI,
    abi_codec: ABICodec,
    fn_identifier: Union[str, Type[FallbackFn], Type[ReceiveFn]] = None,
    args: Sequence[Any] = None,
    kwargs: Any = None,
) -> ABIFunction:
    args = args or tuple()
    kwargs = kwargs or dict()
    num_arguments = len(args) + len(kwargs)

    if fn_identifier is FallbackFn:
        return get_fallback_func_abi(abi)

    if fn_identifier is ReceiveFn:
        return get_receive_func_abi(abi)

    if not is_text(fn_identifier):
        raise TypeError("Unsupported function identifier")

    name_filter = functools.partial(filter_by_name, fn_identifier)
    arg_count_filter = functools.partial(filter_by_argument_count,
                                         num_arguments)
    encoding_filter = functools.partial(filter_by_encodability, abi_codec,
                                        args, kwargs)

    function_candidates = pipe(abi, name_filter, arg_count_filter,
                               encoding_filter)

    if len(function_candidates) == 1:
        return function_candidates[0]
    else:
        matching_identifiers = name_filter(abi)
        matching_function_signatures = [
            abi_to_signature(func) for func in matching_identifiers
        ]

        arg_count_matches = len(arg_count_filter(matching_identifiers))
        encoding_matches = len(encoding_filter(matching_identifiers))

        if arg_count_matches == 0:
            diagnosis = "\nFunction invocation failed due to improper number of arguments."
        elif encoding_matches == 0:
            diagnosis = "\nFunction invocation failed due to no matching argument types."
        elif encoding_matches > 1:
            diagnosis = (
                "\nAmbiguous argument encoding. "
                "Provided arguments can be encoded to multiple functions matching this call."
            )

        message = (
            "\nCould not identify the intended function with name `{name}`, "
            "positional argument(s) of type `{arg_types}` and "
            "keyword argument(s) of type `{kwarg_types}`."
            "\nFound {num_candidates} function(s) with the name `{name}`: {candidates}"
            "{diagnosis}").format(
                name=fn_identifier,
                arg_types=tuple(map(type, args)),
                kwarg_types=valmap(type, kwargs),
                num_candidates=len(matching_identifiers),
                candidates=matching_function_signatures,
                diagnosis=diagnosis,
            )

        raise ValidationError(message)
Beispiel #9
0
def validate_private_key(value):
    if not is_text(value) or not is_hex(value) or not len(
            remove_0x_prefix(value)) == 64:
        raise ValidationError(
            "Private keys must be 32 bytes encoded as hexidecimal")
def bytes32_contract(web3, Bytes32Contract, request, address_conversion_func):
    if is_text(request.param) and request.param[:2] != '0x':
        with pytest.warns(DeprecationWarning):
            return deploy(web3, Bytes32Contract, address_conversion_func, args=[request.param])
    else:
        return deploy(web3, Bytes32Contract, address_conversion_func, args=[request.param])
Beispiel #11
0
def validate_text(value):
    if not is_text(value):
        raise ValidationError(
            "Value must be a text string.  Got type: {}".format(type(value)))
Beispiel #12
0
def is_32byte_hex_string(value):
    return is_text(value) and is_hex(value) and len(
        remove_0x_prefix(value)) == 64
Beispiel #13
0
 def validate_value(cls, value):
     if not is_text(value):
         cls.invalidate_value(value)
Beispiel #14
0
def validate_raw_transaction(raw_transaction):
    if not is_text(raw_transaction) or not is_hex(raw_transaction):
        raise ValidationError(
            "Raw Transaction must be a hexidecimal encoded string.  Got: "
            "{}".format(raw_transaction)
        )
Beispiel #15
0
def validate_transaction(value, txn_type):
    if txn_type not in ALLOWED_TRANSACTION_TYPES:
        raise TypeError("the `txn_type` parameter must be one of send/call/estimate")
    if not is_dict(value):
        raise ValidationError("Transaction must be a dictionary.  Got: {}".format(type(value)))

    unknown_keys = tuple(sorted(set(value.keys()).difference(
        TRANSACTION_TYPE_INFO[txn_type],
    )))
    if unknown_keys:
        raise ValidationError(
            "Only the keys '{}' are allowed.  Got extra keys: '{}'".format(
                "/".join(tuple(sorted(TRANSACTION_TYPE_INFO[txn_type]))),
                "/".join(unknown_keys),
            )
        )

    if txn_type == 'send':
        required_keys = {'from', 'gas'}
    elif txn_type == 'send_signed':
        required_keys = {'from', 'gas'} | SIGNED_TRANSACTION_KEYS
    elif txn_type in {'estimate', 'call'}:
        required_keys = {'from'}
    else:
        raise Exception("Invariant: code path should be unreachable")

    missing_required_keys = tuple(sorted(required_keys.difference(value.keys())))
    if missing_required_keys:
        raise ValidationError(
            "Transaction is missing the required keys: '{}'".format(
                "/".join(missing_required_keys),
            )
        )

    if 'from' in value:
        validate_account(value['from'])

    if 'to' in value and value['to'] != '':
        validate_account(value['to'])
    elif 'to' in value and value['to'] == '':
        validate_text(value['to'])

    if 'gas' in value:
        validate_uint256(value['gas'])

    if 'gas_price' in value:
        validate_uint256(value['gas_price'])

    if 'value' in value:
        validate_uint256(value['value'])

    if 'nonce' in value:
        validate_uint256(value['nonce'])

    if 'data' in value:
        bad_data_message = (
            "Transaction data must be a hexidecimal encoded string.  Got: "
            "{}".format(value['data'])
        )
        if not is_text(value['data']):
            raise ValidationError(bad_data_message)
        elif not remove_0x_prefix(value['data']):
            pass
        elif not is_hex(value['data']):
            raise ValidationError(bad_data_message)
        try:
            decode_hex(value['data'])
        except (binascii.Error, TypeError):
            # TypeError is for python2
            # binascii.Error is for python3
            raise ValidationError(bad_data_message)

    if txn_type == 'send_signed':
        validate_uint256(value['r'])
        validate_uint256(value['s'])
        validate_uint8(value['v'])
 def validate_value(cls, value):
     if not is_text(value):
         cls.invalidate_value(value)
Beispiel #17
0
def key_encoder(key) -> bytes:
    if is_text(key):
        return to_bytes(text=key)
    return key
Beispiel #18
0
def process_type(raw_type):
    if not is_text(raw_type):
        raise TypeError("The type must be a text string.  Got {0}".format(type(raw_type)))
    typ = normalize_type(raw_type)
    return process_strict_type(typ)
Beispiel #19
0
def validate_transaction(value, txn_internal_type):
    if txn_internal_type not in ALLOWED_TRANSACTION_INTERNAL_TYPES:
        raise TypeError(
            "the `txn_internal_type` parameter must be one of send/call/estimate"
        )
    if not is_dict(value):
        raise ValidationError(
            "Transaction must be a dictionary. Got: {}".format(type(value)))

    unknown_keys = tuple(
        sorted(
            set(value.keys()).difference(
                TRANSACTION_INTERNAL_TYPE_INFO[txn_internal_type], )))
    if unknown_keys:
        raise ValidationError(
            "Only the keys '{}' are allowed.  Got extra keys: '{}'".format(
                "/".join(
                    tuple(
                        sorted(
                            TRANSACTION_INTERNAL_TYPE_INFO[txn_internal_type]))
                ),
                "/".join(unknown_keys),
            ))

    if txn_internal_type == 'send':
        required_keys = {'from', 'gas'}
    elif txn_internal_type == 'send_signed':
        required_keys = {'from', 'gas'} | SIGNED_TRANSACTION_KEYS
    elif txn_internal_type in {'estimate', 'call'}:
        required_keys = {'from'}
    else:
        raise Exception("Invariant: code path should be unreachable")

    missing_required_keys = tuple(
        sorted(required_keys.difference(value.keys())))
    if missing_required_keys:
        raise ValidationError(
            "Transaction is missing the required keys: '{}'".format(
                "/".join(missing_required_keys), ))

    if 'type' in value:
        # type is validated but not required. If this value exists, it will be popped out of the
        # dict and the type will instead be inferred from the transaction params.
        validate_transaction_type(value['type'])

    if 'from' in value:
        validate_account(value['from'])

    if 'to' in value and value['to'] != '':
        validate_account(value['to'])
    elif 'to' in value and value['to'] == '':
        validate_text(value['to'])

    if 'gas' in value:
        validate_uint256(value['gas'])

    if 'gas_price' in value:
        validate_uint256(value['gas_price'])

    if 'max_fee_per_gas' in value:
        validate_uint256(value['max_fee_per_gas'])
        if 'gas_price' in value:
            raise ValidationError(
                'Mixed legacy and dynamic fee transaction values')

    if 'max_priority_fee_per_gas' in value:
        validate_uint256(value['max_priority_fee_per_gas'])
        if 'gas_price' in value:
            raise ValidationError(
                'Mixed legacy and dynamic fee transaction values')

    if 'value' in value:
        validate_uint256(value['value'])

    if 'nonce' in value:
        validate_uint256(value['nonce'])

    if 'data' in value:
        bad_data_message = (
            "Transaction 'data' must be a hexadecimal encoded string.  Got: "
            "{}".format(value['data']))
        if not is_text(value['data']):
            raise ValidationError(bad_data_message)
        elif not remove_0x_prefix(value['data']):
            pass
        elif not is_hex(value['data']):
            raise ValidationError(bad_data_message)
        try:
            decode_hex(value['data'])
        except (binascii.Error, TypeError):
            # TypeError is for python2
            # binascii.Error is for python3
            raise ValidationError(bad_data_message)

    if 'access_list' in value:
        _validate_inbound_access_list(value['access_list'])

    if txn_internal_type == 'send_signed':
        validate_uint256(value['r'])
        validate_uint256(value['s'])
        validate_uint8(value['v'])