def decrypt_auth_response( self, auth_response_key: AES128Key ) -> Tuple[bytes, Optional[ENRAPI]]: """Extract id nonce signature and optional ENR from auth header packet.""" plain_text = aesgcm_decrypt( key=auth_response_key, nonce=ZERO_NONCE, cipher_text=self.auth_header.encrypted_auth_response, authenticated_data=b"", ) try: decoded_rlp = rlp.decode(plain_text) except DecodingError: raise ValidationError( f"Auth response does not contain valid RLP: {encode_hex(plain_text)}" ) if not is_list_like(decoded_rlp): raise ValidationError( f"Auth response contains bytes instead of list: {encode_hex(decoded_rlp)}" ) if len(decoded_rlp) != 3: raise ValidationError( f"Auth response is a list of {len(decoded_rlp)} instead of three elements" ) version_bytes, id_nonce_signature, serialized_enr = decoded_rlp if not is_bytes(version_bytes): raise ValidationError( f"Version is a list instead of big endian encoded integer: {version_bytes}" ) version_int = big_endian_to_int(version_bytes) if version_int != AUTH_RESPONSE_VERSION: raise ValidationError( f"Expected auth response version {AUTH_RESPONSE_VERSION}, but got {version_int}" ) if not is_bytes(id_nonce_signature): raise ValidationError( f"Id nonce signature is a list instead of bytes: {id_nonce_signature}" ) if not is_list_like(serialized_enr): raise ValidationError( f"ENR is bytes instead of list: {encode_hex(serialized_enr)}" ) if len(serialized_enr) == 0: enr = None else: try: enr = ENR.deserialize(serialized_enr) except DeserializationError as error: raise ValidationError( "ENR in auth response is not properly encoded" ) from error return id_nonce_signature, enr
def _decode_auth(encoded_packet: bytes) -> Tuple[Union[AuthHeader, Nonce], int]: try: decoded_auth, _, message_start_index = rlp.codec.consume_item(encoded_packet, TAG_SIZE) except DecodingError as error: raise ValidationError("Packet authentication section is not proper RLP") from error if is_bytes(decoded_auth): validate_nonce(decoded_auth) return Nonce(decoded_auth), message_start_index elif is_list_like(decoded_auth): validate_length(decoded_auth, 5, "auth header") for index, element in enumerate(decoded_auth): if not is_bytes(element): raise ValidationError(f"Element {index} in auth header is not bytes: {element}") auth_header = AuthHeader( auth_tag=decoded_auth[0], id_nonce=decoded_auth[1], auth_scheme_name=decoded_auth[2], ephemeral_public_key=decoded_auth[3], encrypted_auth_response=decoded_auth[4], ) validate_auth_header(auth_header) return auth_header, message_start_index else: raise Exception("unreachable: RLP can only encode bytes and lists")
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 test_encode_bytes_xx(bytes_value, value_bit_size, data_byte_size): if value_bit_size > data_byte_size * 8: with pytest.raises(ValueError): AddressEncoder( value_bit_size=value_bit_size, data_byte_size=data_byte_size, ) return encoder = BytesEncoder( value_bit_size=value_bit_size, data_byte_size=data_byte_size, ) if not is_bytes(bytes_value): with pytest.raises(EncodingTypeError) as exception_info: encoder(bytes_value) assert 'BytesEncoder' in str(exception_info.value) return elif len(bytes_value) * 8 > value_bit_size: with pytest.raises(ValueOutOfBounds): encoder(bytes_value) return expected_value = zpad_right(bytes_value, data_byte_size) encoded_value = encoder(bytes_value) assert encoded_value == expected_value
def decode_abi(self, types: Iterable[TypeStr], data: Decodable) -> Tuple[Any, ...]: """ Decodes the binary value ``data`` as a sequence of values of the ABI types in ``types`` via the head-tail mechanism into a tuple of equivalent python values. :param types: An iterable of string representations of the ABI types that will be used for decoding e.g. ``('uint256', 'bytes[]', '(int,int)')`` :param data: The binary value to be decoded. :returns: A tuple of equivalent python values for the ABI values represented in ``data``. """ if not is_bytes(data): raise TypeError("The `data` value must be of bytes type. Got {0}".format(type(data))) decoders = [ self._registry.get_decoder(type_str) for type_str in types ] decoder = TupleDecoder(decoders=decoders) stream = ContextFramesBytesIO(data) return decoder(stream)
def force_text(value): if is_text(value): return value elif is_bytes(value): return codecs.decode(value, "iso-8859-1") else: raise TypeError("Unsupported type: {0}".format(type(value)))
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), ))
def hex_encode_abi_type(abi_type: TypeStr, value: Any, force_size: int = None) -> HexStr: """ Encodes value into a hex string in format of abi_type """ validate_abi_type(abi_type) validate_abi_value(abi_type, value) data_size = force_size or size_of_type(abi_type) if is_array_type(abi_type): sub_type = sub_type_of_array_type(abi_type) return HexStr("".join([ remove_0x_prefix(hex_encode_abi_type(sub_type, v, 256)) for v in value ])) elif is_bool_type(abi_type): return to_hex_with_size(value, data_size) elif is_uint_type(abi_type): return to_hex_with_size(value, data_size) elif is_int_type(abi_type): return to_hex_twos_compliment(value, data_size) elif is_address_type(abi_type): return pad_hex(value, data_size) elif is_bytes_type(abi_type): if is_bytes(value): return encode_hex(value) else: return value elif is_string_type(abi_type): return to_hex(text=value) else: raise ValueError("Unsupported ABI type: {0}".format(abi_type))
def test_eth_sign(self, webu, unlocked_account): signature = webu.eth.sign(unlocked_account, text='Message tö sign. Longer than hash!') assert is_bytes(signature) assert len(signature) == 32 + 32 + 1 # test other formats hexsign = webu.eth.sign( unlocked_account, hexstr= '0x4d6573736167652074c3b6207369676e2e204c6f6e676572207468616e206861736821' ) assert hexsign == signature intsign = webu.eth.sign( unlocked_account, 0x4d6573736167652074c3b6207369676e2e204c6f6e676572207468616e206861736821 ) assert intsign == signature bytessign = webu.eth.sign( unlocked_account, b'Message t\xc3\xb6 sign. Longer than hash!') assert bytessign == signature new_signature = webu.eth.sign(unlocked_account, text='different message is different') assert new_signature != signature
def decode(rlp, sedes=None, strict=True, recursive_cache=False, **kwargs): """Decode an RLP encoded object. If the deserialized result `obj` has an attribute :attr:`_cached_rlp` (e.g. if `sedes` is a subclass of :class:`rlp.Serializable`) it will be set to `rlp`, which will improve performance on subsequent :func:`rlp.encode` calls. Bear in mind however that `obj` needs to make sure that this value is updated whenever one of its fields changes or prevent such changes entirely (:class:`rlp.sedes.Serializable` does the latter). :param sedes: an object implementing a function ``deserialize(code)`` which will be applied after decoding, or ``None`` if no deserialization should be performed :param \*\*kwargs: additional keyword arguments that will be passed to the deserializer :param strict: if false inputs that are longer than necessary don't cause an exception :returns: the decoded and maybe deserialized Python object :raises: :exc:`rlp.DecodingError` if the input string does not end after the root item and `strict` is true :raises: :exc:`rlp.DeserializationError` if the deserialization fails """ if not is_bytes(rlp): raise DecodingError('Can only decode RLP bytes, got type %s' % type(rlp).__name__, rlp) try: item, per_item_rlp, end = consume_item(rlp, 0) except IndexError: raise DecodingError('RLP string too short', rlp) if end != len(rlp) and strict: msg = 'RLP string ends with {} superfluous bytes'.format(len(rlp) - end) raise DecodingError(msg, rlp) if sedes: obj = sedes.deserialize(item, **kwargs) if is_sequence(obj) or hasattr(obj, '_cached_rlp'): _apply_rlp_cache(obj, per_item_rlp, recursive_cache) return obj else: return item
def encode_if_not_bytes(value): if is_bytes(value): return value elif is_text(value): return value.encode() else: raise TypeError("Expected string type: Got {0}".format(type(value)))
def hex_encode_abi_type(abi_type, value, force_size=None): """ Encodes value into a hex string in format of abi_type """ validate_abi_type(abi_type) validate_abi_value(abi_type, value) data_size = force_size or size_of_type(abi_type) if is_array_type(abi_type): sub_type = sub_type_of_array_type(abi_type) return "".join([remove_0x_prefix(hex_encode_abi_type(sub_type, v, 256)) for v in value]) elif is_bool_type(abi_type): return to_hex_with_size(value, data_size) elif is_uint_type(abi_type): return to_hex_with_size(value, data_size) elif is_int_type(abi_type): return to_hex_twos_compliment(value, data_size) elif is_address_type(abi_type): return pad_hex(value, data_size) elif is_bytes_type(abi_type): if is_bytes(value): return encode_hex(value) else: return value elif is_string_type(abi_type): return encode_hex(value) else: raise ValueError( "Unsupported ABI type: {0}".format(abi_type) )
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 validate_value(self, value: Any) -> bytes: # type: ignore if not is_bytes(value) and not is_text(value): self.invalidate_value(value) raw_value = value if is_text(value): try: value = decode_hex(value) except binascii.Error: self.invalidate_value( value, msg=f'{value} is not a valid hex string', ) else: if raw_value[:2] != '0x': self.invalidate_value( raw_value, msg='hex string must be prefixed with 0x') byte_size = self.value_bit_size // 8 if len(value) > byte_size: self.invalidate_value( value, exc=ValueOutOfBounds, msg="exceeds total byte size for bytes{} encoding".format( byte_size), ) elif len(value) < byte_size: self.invalidate_value( value, exc=ValueOutOfBounds, msg="less than total byte size for bytes{} encoding".format( byte_size), ) return value
def bytes_repr(value): if is_bytes(value): yield value elif is_text(value): yield to_bytes(text=value) elif is_list_like(value): yield b''.join(( b'(', b','.join(bytes_repr(item) for item in value), b')', )) elif is_dict(value): yield b''.join(( b'{', b','.join(( b":".join((bytes_repr(key), bytes_repr(item))) for key, item in value.items() )), b'}', )) elif is_integer(value): yield to_bytes(value) elif is_null(value): yield 'None@{}'.format(id(value)) else: raise TypeError("Unsupported type for bytes_repr: {}".format(type(value)))
def decode_abi(self, types: Iterable[TypeStr], data: Decodable) -> Tuple[Any, ...]: """ Decodes the binary value ``data`` as a sequence of values of the ABI types in ``types`` via the head-tail mechanism into a tuple of equivalent python values. :param types: An iterable of string representations of the ABI types that will be used for decoding e.g. ``('uint256', 'bytes[]', '(int,int)')`` :param data: The binary value to be decoded. :returns: A tuple of equivalent python values for the ABI values represented in ``data``. """ if not is_bytes(data): raise TypeError( "The `data` value must be of bytes type. Got {0}".format( type(data))) decoders = [self._registry.get_decoder(type_str) for type_str in types] decoder = TupleDecoder(decoders=decoders) stream = self.stream_class(data) return decoder(stream)
def stringify(value): if is_bytes(value): yield value elif is_text(value): yield force_bytes(value) elif is_list_like(value): yield b''.join(( b'(', b','.join((stringify(item) for item in value)), b')', )) elif is_dict(value): yield b''.join(( b'{', b','.join((b":".join((stringify(key), stringify(item))) for key, item in value.items())), b'}', )) elif is_integer(value): yield force_bytes(str(value)) elif is_null(value): yield 'None@{0}'.format(id(value)) else: raise TypeError("Unsupported type for stringification: {0}".format( type(value)))
def validate_value(cls, value): if not is_bytes(value): raise EncodingTypeError( "Value of type {} cannot be encoded by {}".format( type(value), cls.__name__, ))
def normalize_bytes(value: Any) -> bytes: if is_bytes(value): return value elif is_text(value) and is_hex(value): return decode_hex(value) else: raise TypeError("Value must be either a string or bytes object")
def decode_message_packet( encoded_packet: bytes) -> Union[AuthTagPacket, AuthHeaderPacket]: validate_message_packet_size(encoded_packet) tag = _decode_tag(encoded_packet) auth, message_start_index = _decode_auth(encoded_packet) encrypted_message = encoded_packet[message_start_index:] packet: Union[AuthTagPacket, AuthHeaderPacket] if is_bytes(auth): packet = AuthTagPacket( tag=tag, auth_tag=cast(Nonce, auth), encrypted_message=encrypted_message, ) elif isinstance(auth, AuthHeader): packet = AuthHeaderPacket( tag=tag, auth_header=auth, encrypted_message=encrypted_message, ) else: raise Exception( "Unreachable: decode_auth returns either Nonce or AuthHeader") return packet
def __eq__(self, other: Any) -> bool: if hasattr(other, 'to_bytes'): return self.to_bytes() == other.to_bytes() elif is_bytes(other): return self.to_bytes() == other else: return False
def test_platon_sign(self, unlocked_account): platon = Eth(unlocked_account['node'].web3) signature = platon.sign( unlocked_account['address'], text='Message tö sign. Longer than hash!' ) assert is_bytes(signature) assert len(signature) == 32 + 32 + 1 # test other formats hexsign = platon.sign( unlocked_account['address'], hexstr='0x4d6573736167652074c3b6207369676e2e204c6f6e676572207468616e206861736821' ) assert hexsign == signature intsign = platon.sign( unlocked_account['address'], 0x4d6573736167652074c3b6207369676e2e204c6f6e676572207468616e206861736821 ) assert intsign == signature bytessign = platon.sign( unlocked_account['address'], b'Message t\xc3\xb6 sign. Longer than hash!' ) assert bytessign == signature new_signature = platon.sign( unlocked_account['address'], text='different message is different' ) assert new_signature != signature
def normalize_bytes(value): if is_hex(value) or len(value) == 0: return decode_hex(value) elif is_bytes(value): return value else: raise TypeError("Value must be either a string or bytes object")
def test_eth_sign(self, web3: "Web3", unlocked_account_dual_type: ChecksumAddress) -> None: signature = web3.eth.sign(unlocked_account_dual_type, text='Message tö sign. Longer than hash!') assert is_bytes(signature) assert len(signature) == 32 + 32 + 1 # test other formats hexsign = web3.eth.sign( unlocked_account_dual_type, hexstr=HexStr( '0x4d6573736167652074c3b6207369676e2e204c6f6e676572207468616e206861736821' )) assert hexsign == signature intsign = web3.eth.sign( unlocked_account_dual_type, 0x4d6573736167652074c3b6207369676e2e204c6f6e676572207468616e206861736821 ) assert intsign == signature bytessign = web3.eth.sign( unlocked_account_dual_type, b'Message t\xc3\xb6 sign. Longer than hash!') assert bytessign == signature new_signature = web3.eth.sign(unlocked_account_dual_type, text='different message is different') assert new_signature != signature
def decode_single(typ: TypeStr, data: Decodable) -> Any: """ Decodes the binary value ``data`` of the ABI type ``typ`` into its equivalent python value. :param typ: The string representation of the ABI type that will be used for decoding e.g. ``'uint256'``, ``'bytes[]'``, ``'(int,int)'``, etc. :param data: The binary value to be decoded. :returns: The equivalent python value of the ABI value represented in ``data``. """ if not is_bytes(data): raise TypeError( "The `data` value must be of bytes type. Got {0}".format( type(data))) if isinstance(typ, str): type_str = typ else: type_str = collapse_type(*typ) decoder = registry.get_decoder(type_str) stream = ContextFramesBytesIO(data) return decoder(stream)
def serialize_block(evm, block, transaction_serialize_fn, is_pending): transactions = [ transaction_serialize_fn(block, transaction, transaction_index, is_pending) for transaction_index, transaction in enumerate(block.transactions if is_pyethereum21_available( ) else block.transaction_list) ] # NOTE: Hack to compute total difficulty for pyethereum 2.0 # As far as I could tell, this didn't really do anything in 1.6 if hasattr(block, 'chain_difficulty'): total_difficulty = block.chain_difficulty() elif hasattr(evm, 'chain') and hasattr(evm.chain, 'get_score'): total_difficulty = evm.chain.get_score(block) else: raise Exception( 'Invariant: failed to match pyethereum16 or pyethereum21 API') return { "number": block.number, "hash": block.hash, "parent_hash": block.prevhash, "nonce": zpad(encode_if_not_bytes(block.nonce), 8), "sha3_uncles": block.uncles_hash, "logs_bloom": block.bloom, "transactions_root": block.tx_list_root, "receipts_root": block.receipts_root, "state_root": block.state_root, "miner": block.coinbase if is_bytes(block.coinbase) else block.coinbase.encode(), "difficulty": block.difficulty, "total_difficulty": total_difficulty, "size": len(rlp.encode(block)), "extra_data": zpad32(encode_if_not_bytes(block.extra_data)), "gas_limit": block.gas_limit, "gas_used": block.gas_used, "timestamp": block.timestamp, "transactions": transactions, "uncles": block.uncles, }
def test_eth_sign_ens_names( self, web3: "Web3", unlocked_account_dual_type: ChecksumAddress) -> None: with ens_addresses(web3, {'unlocked-acct.eth': unlocked_account_dual_type}): signature = web3.eth.sign( 'unlocked-acct.eth', text='Message tö sign. Longer than hash!') assert is_bytes(signature) assert len(signature) == 32 + 32 + 1
def normalize_bytes(value: Union[bytes, str]) -> bytes: if is_bytes(value): return cast(bytes, value) elif is_text(value) and is_hex(value): return decode_hex(cast(str, value)) elif is_text(value): return b'' else: raise TypeError("Value must be either a string or bytes object")
def decode_abi(types, data): if not is_bytes(data): raise TypeError( "The `data` value must be of bytes type. Got {0}".format( type(data))) processed_types = tuple(process_type(_type) for _type in types) decoder = get_multi_decoder(processed_types) stream = BytesIO(data) return decoder(stream)
def decode(ssz, sedes): """ Decode a SSZ encoded object. """ if not is_bytes(ssz): raise TypeError( f"Can only decode SSZ bytes, got type {type(ssz).__name__}") value = sedes.deserialize(ssz) return value
def decode(ssz, sedes): """ Decode a SSZ encoded object. """ if not is_bytes(ssz): raise DecodingError( 'Can only decode SSZ bytes, got type %s' % type(ssz).__name__, ssz) obj = sedes.deserialize(ssz) return obj
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 sys.version_info.major >= 3 and 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_value(self, value): if not is_bytes(value): self.invalidate_value(value) byte_size = self.value_bit_size // 8 if len(value) > byte_size: self.invalidate_value( value, exc=ValueOutOfBounds, msg="exceeds total byte size for bytes{} encoding".format(byte_size), )
def validate_value(cls, value): if is_bytes(value): try: value = to_text(value) except UnicodeDecodeError: cls.invalidate_value( value, msg='not decodable as unicode string', ) super().validate_value(value)
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"}
def decode_single(self, typ: TypeStr, data: Decodable) -> Any: """ Decodes the binary value ``data`` of the ABI type ``typ`` into its equivalent python value. :param typ: The string representation of the ABI type that will be used for decoding e.g. ``'uint256'``, ``'bytes[]'``, ``'(int,int)'``, etc. :param data: The binary value to be decoded. :returns: The equivalent python value of the ABI value represented in ``data``. """ if not is_bytes(data): raise TypeError("The `data` value must be of bytes type. Got {0}".format(type(data))) decoder = self._registry.get_decoder(typ) stream = ContextFramesBytesIO(data) return decoder(stream)
def test_encode_byte_string(string_value): encoder = ByteStringEncoder() if not is_bytes(string_value): with pytest.raises(EncodingTypeError) as exception_info: encoder(string_value) assert 'ByteStringEncoder' in str(exception_info.value) return expected_value = ( encode_uint_256(len(string_value)) + ( zpad_right(string_value, ceil32(len(string_value))) if string_value else b'\x00' * 32 ) ) encoded_value = encoder(string_value) assert encoded_value == expected_value
def test_eth_sign(self, web3, unlocked_account): signature = web3.eth.sign(unlocked_account, text='Message tö sign. Longer than hash!') assert is_bytes(signature) assert len(signature) == 32 + 32 + 1 # test other formats hexsign = web3.eth.sign( unlocked_account, hexstr='0x4d6573736167652074c3b6207369676e2e204c6f6e676572207468616e206861736821' ) assert hexsign == signature intsign = web3.eth.sign( unlocked_account, 0x4d6573736167652074c3b6207369676e2e204c6f6e676572207468616e206861736821 ) assert intsign == signature bytessign = web3.eth.sign(unlocked_account, b'Message t\xc3\xb6 sign. Longer than hash!') assert bytessign == signature new_signature = web3.eth.sign(unlocked_account, text='different message is different') assert new_signature != signature
def validate_value(cls, value): if not is_bytes(value): cls.invalidate_value(value)
def is_prefixed(value, prefix): return value.startswith( force_bytes(prefix) if is_bytes(value) else force_text(prefix) )