def test_decode_bytes_xx(value_byte_size, stream_bytes, data_byte_size): if value_byte_size > data_byte_size: with pytest.raises(ValueError): BytesDecoder( value_bit_size=value_byte_size * 8, data_byte_size=data_byte_size, ) return else: decoder = BytesDecoder( value_bit_size=value_byte_size * 8, data_byte_size=data_byte_size, ) stream = ContextFramesBytesIO(stream_bytes) actual_value = stream_bytes[:value_byte_size] padding_bytes = stream_bytes[value_byte_size:data_byte_size] if len(stream_bytes) < data_byte_size: with pytest.raises(InsufficientDataBytes): decoder(stream) return elif is_non_empty_non_null_byte_string(padding_bytes): with pytest.raises(NonEmptyPaddingBytes): decoder(stream) return else: decoded_value = decoder(stream) assert decoded_value == actual_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 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 test_decode_unsigned_fixed(value_bit_size, frac_places, stream_bytes, data_byte_size): if value_bit_size > data_byte_size * 8: with pytest.raises(ValueError): UnsignedFixedDecoder( value_bit_size=value_bit_size, frac_places=frac_places, data_byte_size=data_byte_size, ) return decoder = UnsignedFixedDecoder( value_bit_size=value_bit_size, frac_places=frac_places, data_byte_size=data_byte_size, ) stream = ContextFramesBytesIO(stream_bytes) padding_bytes = stream_bytes[:data_byte_size][:data_byte_size - value_bit_size // 8] if len(stream_bytes) < data_byte_size: with pytest.raises(InsufficientDataBytes): decoder(stream) return if is_non_empty_non_null_byte_string(padding_bytes): with pytest.raises(NonEmptyPaddingBytes): decoder(stream) return # Ensure no exceptions decoder(stream)
def test_decode_boolean(stream_bytes, data_byte_size): stream = ContextFramesBytesIO(stream_bytes) decoder = BooleanDecoder(data_byte_size=data_byte_size) if len(stream_bytes) < data_byte_size: with pytest.raises(InsufficientDataBytes): decoder(stream) return padding_bytes = stream_bytes[:data_byte_size][:-1] if is_non_empty_non_null_byte_string(padding_bytes): with pytest.raises(NonEmptyPaddingBytes): decoder(stream) return byte_value = stream_bytes[data_byte_size - 1] if byte_value in {0, b'\x00'}: actual_value = False elif byte_value in {1, b'\x01'}: actual_value = True else: with pytest.raises(NonEmptyPaddingBytes): decoder(stream) return decoded_value = decoder(stream) assert decoded_value is actual_value
def test_decode_address(address_bytes, padding_size, data_byte_size): stream_bytes = b'\x00' * padding_size + address_bytes if data_byte_size < 20: with pytest.raises(ValueError): AddressDecoder(data_byte_size=data_byte_size, ) return else: decoder = AddressDecoder(data_byte_size=data_byte_size, ) stream = ContextFramesBytesIO(stream_bytes) padding_bytes = stream_bytes[:data_byte_size][:-20] if len(stream_bytes) < data_byte_size: with pytest.raises(InsufficientDataBytes): decoder(stream) return elif is_non_empty_non_null_byte_string(padding_bytes): with pytest.raises(NonEmptyPaddingBytes): decoder(stream) return else: decoded_value = decoder(stream) actual_value = to_normalized_address(stream_bytes[:data_byte_size][-20:]) assert decoded_value == actual_value
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))) decoders = [registry.get_decoder(type_str) for type_str in types] decoder = TupleDecoder(decoders=decoders) stream = ContextFramesBytesIO(data) return decoder(stream)
def test_decode_unsigned_real(high_bit_size, low_bit_size, integer_bit_size, stream_bytes, data_byte_size): if integer_bit_size > data_byte_size * 8: with pytest.raises(ValueError): UnsignedRealDecoder( value_bit_size=integer_bit_size, high_bit_size=high_bit_size, low_bit_size=low_bit_size, data_byte_size=data_byte_size, ) return elif high_bit_size + low_bit_size != integer_bit_size: with pytest.raises(ValueError): UnsignedRealDecoder( value_bit_size=integer_bit_size, high_bit_size=high_bit_size, low_bit_size=low_bit_size, data_byte_size=data_byte_size, ) return else: decoder = UnsignedRealDecoder( value_bit_size=integer_bit_size, high_bit_size=high_bit_size, low_bit_size=low_bit_size, data_byte_size=data_byte_size, ) stream = ContextFramesBytesIO(stream_bytes) padding_bytes = stream_bytes[:data_byte_size][:data_byte_size - integer_bit_size // 8] if len(stream_bytes) < data_byte_size: with pytest.raises(InsufficientDataBytes): decoder(stream) return elif is_non_empty_non_null_byte_string(padding_bytes): with pytest.raises(NonEmptyPaddingBytes): decoder(stream) return else: decoded_value = decoder(stream) unsigned_integer_value = big_endian_to_int(stream_bytes[:data_byte_size]) with decimal.localcontext(abi_decimal_context): raw_real_value = decimal.Decimal(unsigned_integer_value) / 2 ** low_bit_size actual_value = quantize_value(raw_real_value, low_bit_size) assert decoded_value == actual_value
def decode_single(typ, 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 test_decode_strings_raises(_bytes, pad_size): size_bytes = zpad32(int_to_big_endian(len(_bytes))) padded_bytes = _bytes + b'\x00' * pad_size stream_bytes = size_bytes + padded_bytes stream = ContextFramesBytesIO(stream_bytes) decoder = StringDecoder() if len(padded_bytes) < ceil32(len(_bytes)): with pytest.raises(InsufficientDataBytes): decoder(stream) return with pytest.raises(DecodingError): decoder(stream)
def test_decode_strings(_strings, pad_size): size_bytes = zpad32(int_to_big_endian(len(_strings.encode("utf-8")))) padded_bytes = _strings.encode("utf-8") + b'\x00' * pad_size stream_bytes = size_bytes + padded_bytes stream = ContextFramesBytesIO(stream_bytes) decoder = StringDecoder() if len(padded_bytes) < ceil32(len(_strings.encode("utf-8"))): with pytest.raises(InsufficientDataBytes): decoder(stream) return decoded_value = decoder(stream) assert decoded_value == _strings
def test_decode_signed_int(integer_bit_size, stream_bytes, data_byte_size): if integer_bit_size % 8 != 0: with pytest.raises(ValueError): SignedIntegerDecoder( value_bit_size=integer_bit_size, data_byte_size=data_byte_size, ) return elif integer_bit_size > data_byte_size * 8: with pytest.raises(ValueError): SignedIntegerDecoder( value_bit_size=integer_bit_size, data_byte_size=data_byte_size, ) return else: decoder = SignedIntegerDecoder( value_bit_size=integer_bit_size, data_byte_size=data_byte_size, ) stream = ContextFramesBytesIO(stream_bytes) padding_bytes = data_byte_size - integer_bit_size // 8 raw_value = big_endian_to_int(stream_bytes[padding_bytes:data_byte_size]) if raw_value >= 2 ** (integer_bit_size - 1): actual_value = raw_value - 2 ** integer_bit_size else: actual_value = raw_value if len(stream_bytes) < data_byte_size: with pytest.raises(InsufficientDataBytes): decoder(stream) return elif ( (actual_value >= 0 and not all_bytes_equal(stream_bytes[:padding_bytes], 0)) or (actual_value < 0 and not all_bytes_equal(stream_bytes[:padding_bytes], 255)) ): with pytest.raises(NonEmptyPaddingBytes): decoder(stream) return else: decoded_value = decoder(stream) assert decoded_value == actual_value
def test_decode_array_of_unsigned_integers(array_size, array_values): size_bytes = zpad32(int_to_big_endian(array_size)) values_bytes = b''.join( (zpad32(int_to_big_endian(v)) for v in array_values)) stream_bytes = size_bytes + values_bytes decoder = DynamicArrayDecoder( item_decoder=UnsignedIntegerDecoder(value_bit_size=256), ) stream = ContextFramesBytesIO(stream_bytes) if len(array_values) < array_size: with pytest.raises(InsufficientDataBytes): decoder(stream) return actual_values = decoder(stream) assert actual_values == array_values[:array_size]
def test_decode_signed_fixed(value_bit_size, frac_places, stream_bytes, data_byte_size): if value_bit_size > data_byte_size * 8: with pytest.raises(ValueError): SignedFixedDecoder( value_bit_size=value_bit_size, frac_places=frac_places, data_byte_size=data_byte_size, ) return decoder = SignedFixedDecoder( value_bit_size=value_bit_size, frac_places=frac_places, data_byte_size=data_byte_size, ) stream = ContextFramesBytesIO(stream_bytes) padding_offset = data_byte_size - value_bit_size // 8 data_offset = padding_offset + value_bit_size // 8 padding_bytes = stream_bytes[:data_byte_size][:padding_offset] data_bytes = stream_bytes[:data_byte_size][padding_offset:data_offset] if len(stream_bytes) < data_byte_size: with pytest.raises(InsufficientDataBytes): decoder(stream) return if not is_valid_padding_bytes(padding_bytes, data_bytes): with pytest.raises(NonEmptyPaddingBytes): decoder(stream) return actual_value = decoder(stream) if padding_bytes: if actual_value >= 0: assert bytes(set(padding_bytes)) == b'\x00' else: assert bytes(set(padding_bytes)) == b'\xff'
def test_decode_unsigned_int(integer_bit_size, stream_bytes, data_byte_size): if integer_bit_size % 8 != 0: with pytest.raises(ValueError): UnsignedIntegerDecoder( value_bit_size=integer_bit_size, data_byte_size=data_byte_size, ) return elif integer_bit_size > data_byte_size * 8: with pytest.raises(ValueError): UnsignedIntegerDecoder( value_bit_size=integer_bit_size, data_byte_size=data_byte_size, ) return else: decoder = UnsignedIntegerDecoder( value_bit_size=integer_bit_size, data_byte_size=data_byte_size, ) stream = ContextFramesBytesIO(stream_bytes) actual_value = big_endian_to_int(stream_bytes[:data_byte_size]) if len(stream_bytes) < data_byte_size: with pytest.raises(InsufficientDataBytes): decoder(stream) return elif actual_value > 2 ** integer_bit_size - 1: with pytest.raises(NonEmptyPaddingBytes): decoder(stream) return else: decoded_value = decoder(stream) assert decoded_value == actual_value
def byte_stream(byte_string): return ContextFramesBytesIO(byte_string)
def test_tuple_decoder(types, data, expected): decoders = [registry.get_decoder(t) for t in types] decoder = TupleDecoder(decoders=decoders) stream = ContextFramesBytesIO(decode_hex(data)) actual = decoder(stream) assert actual == expected
def test_decode_signed_real(high_bit_size, low_bit_size, integer_bit_size, stream_bytes, data_byte_size): if integer_bit_size > data_byte_size * 8: with pytest.raises(ValueError): SignedRealDecoder( value_bit_size=integer_bit_size, high_bit_size=high_bit_size, low_bit_size=low_bit_size, data_byte_size=data_byte_size, ) return elif high_bit_size + low_bit_size != integer_bit_size: with pytest.raises(ValueError): SignedRealDecoder( value_bit_size=integer_bit_size, high_bit_size=high_bit_size, low_bit_size=low_bit_size, data_byte_size=data_byte_size, ) return else: decoder = SignedRealDecoder( value_bit_size=integer_bit_size, high_bit_size=high_bit_size, low_bit_size=low_bit_size, data_byte_size=data_byte_size, ) stream = ContextFramesBytesIO(stream_bytes) padding_offset = data_byte_size - integer_bit_size // 8 data_offset = padding_offset + integer_bit_size // 8 padding_bytes = stream_bytes[:data_byte_size][:padding_offset] data_bytes = stream_bytes[:data_byte_size][padding_offset:data_offset] if len(stream_bytes) < data_byte_size: with pytest.raises(InsufficientDataBytes): decoder(stream) return elif not is_valid_padding_bytes(padding_bytes, data_bytes): with pytest.raises(NonEmptyPaddingBytes): decoder(stream) return else: decoded_value = decoder(stream) if padding_bytes: if decoded_value >= 0: assert bytes(set(padding_bytes)) == b'\x00' else: assert bytes(set(padding_bytes)) == b'\xff' _, upper_bound = compute_signed_integer_bounds(high_bit_size + low_bit_size) unsigned_integer_value = big_endian_to_int(data_bytes) if unsigned_integer_value >= upper_bound: signed_integer_value = unsigned_integer_value - 2**(high_bit_size + low_bit_size) else: signed_integer_value = unsigned_integer_value with decimal.localcontext(abi_decimal_context): raw_actual_value = decimal.Decimal( signed_integer_value) / 2**low_bit_size actual_value = quantize_value(raw_actual_value, low_bit_size) assert decoded_value == actual_value