From 92472bdb66f352ee412bb8d3e3f10c8ffadbd66c Mon Sep 17 00:00:00 2001 From: Arjan Zijderveld <5286904+arjanz@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:54:13 +0200 Subject: [PATCH] Restructured unit tests (#124) Removed ss58, moved to substrate-interface --- scalecodec/types.py | 9 +- scalecodec/utils/ss58.py | 258 ---------------------------------- test/test_array.py | 82 +++++++++++ test/test_bitvec.py | 108 ++++++++++++++ test/test_boolean.py | 52 +++++++ test/test_bytes.py | 71 ++++++++++ test/test_compact.py | 123 ++++++++++++++++ test/test_enum.py | 50 +++++++ test/test_float.py | 53 +++++++ test/test_hashmap.py | 38 +++++ test/test_integer.py | 60 ++++++++ test/test_option.py | 61 ++++++++ test/test_scale_primitives.py | 175 ----------------------- test/test_scale_types.py | 236 +------------------------------ test/test_ss58.py | 216 ---------------------------- test/test_string.py | 57 ++++++++ test/test_tuple.py | 47 +++++++ test/test_type_encoding.py | 189 ------------------------- test/test_vec.py | 52 +++++++ 19 files changed, 861 insertions(+), 1076 deletions(-) delete mode 100644 scalecodec/utils/ss58.py create mode 100644 test/test_array.py create mode 100644 test/test_bitvec.py create mode 100644 test/test_boolean.py create mode 100644 test/test_bytes.py create mode 100644 test/test_compact.py create mode 100644 test/test_enum.py create mode 100644 test/test_float.py create mode 100644 test/test_hashmap.py create mode 100644 test/test_integer.py create mode 100644 test/test_option.py delete mode 100644 test/test_scale_primitives.py delete mode 100644 test/test_ss58.py create mode 100644 test/test_string.py create mode 100644 test/test_tuple.py delete mode 100644 test/test_type_encoding.py create mode 100644 test/test_vec.py diff --git a/scalecodec/types.py b/scalecodec/types.py index 10b66ec..2838fbf 100644 --- a/scalecodec/types.py +++ b/scalecodec/types.py @@ -163,6 +163,7 @@ def deserialize(self, value: bool) -> bool: def example_value(self, _recursion_level: int = 0, max_recursion: int = TYPE_DECOMP_MAX_RECURSIVE): return True + class NullType(ScaleTypeDef): def decode(self, data: ScaleBytes) -> any: @@ -798,7 +799,7 @@ def deserialize(self, value: list) -> list: return [(self.key_def.deserialize(k), self.value_def.deserialize(v)) for k, v in value] -class Bytes(ScaleTypeDef): +class BytesDef(ScaleTypeDef): """ A variable collection of bytes, stored as an `Vec` """ @@ -844,7 +845,7 @@ def example_value(self, _recursion_level: int = 0, max_recursion: int = TYPE_DEC return b'Bytes' -class String(Bytes): +class StringDef(BytesDef): def decode(self, data: ScaleBytes) -> str: value = super().decode(data) @@ -900,8 +901,8 @@ def example_value(self, _recursion_level: int = 0, max_recursion: int = TYPE_DEC return f'0x{str(self.byte_count).zfill(2) * self.byte_count}' -String = String() -Bytes = Bytes() +String = StringDef() +Bytes = BytesDef() Type = String Text = String H256 = HashDef(256) diff --git a/scalecodec/utils/ss58.py b/scalecodec/utils/ss58.py deleted file mode 100644 index cd660ec..0000000 --- a/scalecodec/utils/ss58.py +++ /dev/null @@ -1,258 +0,0 @@ -# Python SCALE Codec Library -# -# Copyright 2018-2021 Stichting Polkascan (Polkascan Foundation). -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ss58.py - -""" SS58 is a simple address format designed for Substrate based chains. - Encoding/decoding according to specification on - https://github.com/paritytech/substrate/wiki/External-Address-Format-(SS58) - -""" -from typing import Optional, Union - -import base58 -from hashlib import blake2b - -from scalecodec.base import ScaleBytes - - -# , RuntimeConfiguration - - -def ss58_decode(address: str, valid_ss58_format: Optional[int] = None) -> str: - - # TODO return bytes in stead of hex-string - - """ - Decodes given SS58 encoded address to an account ID - Parameters - ---------- - address: e.g. EaG2CRhJWPb7qmdcJvy3LiWdh26Jreu9Dx6R1rXxPmYXoDk - valid_ss58_format - - Returns - ------- - Decoded string AccountId - """ - - # Check if address is already decoded - if address.startswith('0x'): - return address - - if address == '': - raise ValueError("Empty address provided") - - checksum_prefix = b'SS58PRE' - - address_decoded = base58.b58decode(address) - - if address_decoded[0] & 0b0100_0000: - ss58_format_length = 2 - ss58_format = ((address_decoded[0] & 0b0011_1111) << 2) | (address_decoded[1] >> 6) | \ - ((address_decoded[1] & 0b0011_1111) << 8) - else: - ss58_format_length = 1 - ss58_format = address_decoded[0] - - if ss58_format in [46, 47]: - raise ValueError(f"{ss58_format} is a reserved SS58 format") - - if valid_ss58_format is not None and ss58_format != valid_ss58_format: - raise ValueError("Invalid SS58 format") - - # Determine checksum length according to length of address string - if len(address_decoded) in [3, 4, 6, 10]: - checksum_length = 1 - elif len(address_decoded) in [5, 7, 11, 34 + ss58_format_length, 35 + ss58_format_length]: - checksum_length = 2 - elif len(address_decoded) in [8, 12]: - checksum_length = 3 - elif len(address_decoded) in [9, 13]: - checksum_length = 4 - elif len(address_decoded) in [14]: - checksum_length = 5 - elif len(address_decoded) in [15]: - checksum_length = 6 - elif len(address_decoded) in [16]: - checksum_length = 7 - elif len(address_decoded) in [17]: - checksum_length = 8 - else: - raise ValueError("Invalid address length") - - checksum = blake2b(checksum_prefix + address_decoded[0:-checksum_length]).digest() - - if checksum[0:checksum_length] != address_decoded[-checksum_length:]: - raise ValueError("Invalid checksum") - - return address_decoded[ss58_format_length:len(address_decoded)-checksum_length].hex() - - -def ss58_encode(address: Union[str, bytes], ss58_format: int = 42) -> str: - """ - Encodes an account ID to an Substrate address according to provided address_type - - Parameters - ---------- - address - ss58_format - - Returns - ------- - str - """ - checksum_prefix = b'SS58PRE' - - if ss58_format < 0 or ss58_format > 16383 or ss58_format in [46, 47]: - raise ValueError("Invalid value for ss58_format") - - if type(address) is bytes or type(address) is bytearray: - address_bytes = address - else: - address_bytes = bytes.fromhex(address.replace('0x', '')) - - if len(address_bytes) in [32, 33]: - # Checksum size is 2 bytes for public key - checksum_length = 2 - elif len(address_bytes) in [1, 2, 4, 8]: - # Checksum size is 1 byte for account index - checksum_length = 1 - else: - raise ValueError("Invalid length for address") - - if ss58_format < 64: - ss58_format_bytes = bytes([ss58_format]) - else: - ss58_format_bytes = bytes([ - ((ss58_format & 0b0000_0000_1111_1100) >> 2) | 0b0100_0000, - (ss58_format >> 8) | ((ss58_format & 0b0000_0000_0000_0011) << 6) - ]) - - input_bytes = ss58_format_bytes + address_bytes - checksum = blake2b(checksum_prefix + input_bytes).digest() - - return base58.b58encode(input_bytes + checksum[:checksum_length]).decode() - - -def ss58_encode_account_index(account_index: int, ss58_format: int = 42) -> str: - """ - Encodes an AccountIndex to an Substrate address according to provided address_type - - Parameters - ---------- - account_index - ss58_format - - Returns - ------- - str - """ - from scalecodec.types import U8, U16, U32, U64 - - if 0 <= account_index <= 2 ** 8 - 1: - account_idx_encoder = U8.new() - elif 2 ** 8 <= account_index <= 2 ** 16 - 1: - account_idx_encoder = U16.new() - elif 2 ** 16 <= account_index <= 2 ** 32 - 1: - account_idx_encoder = U32.new() - elif 2 ** 32 <= account_index <= 2 ** 64 - 1: - account_idx_encoder = U64.new() - else: - raise ValueError("Value too large for an account index") - - return ss58_encode(account_idx_encoder.encode(account_index).data, ss58_format) - - -def ss58_decode_account_index(address: str, valid_ss58_format: Optional[int] = None) -> int: - """ - Decodes given SS58 encoded address to an AccountIndex - - Parameters - ---------- - address - valid_ss58_format - - Returns - ------- - Decoded int AccountIndex - """ - from scalecodec.types import U8, U16, U32, U64 - - account_index_bytes = ss58_decode(address, valid_ss58_format) - - if len(account_index_bytes) == 2: - return U8.decode(ScaleBytes('0x{}'.format(account_index_bytes))) - if len(account_index_bytes) == 4: - return U16.decode(ScaleBytes('0x{}'.format(account_index_bytes))) - if len(account_index_bytes) == 8: - return U32.decode(ScaleBytes('0x{}'.format(account_index_bytes))) - if len(account_index_bytes) == 16: - return U64.decode(ScaleBytes('0x{}'.format(account_index_bytes))) - else: - raise ValueError("Invalid account index length") - - -def is_valid_ss58_address(value: str, valid_ss58_format: Optional[int] = None) -> bool: - """ - Checks if given value is a valid SS58 formatted address, optionally check if address is valid for specified - ss58_format - - Parameters - ---------- - value: value to checked - valid_ss58_format: if valid_ss58_format is provided the address must be valid for specified ss58_format (network) as well - - Returns - ------- - bool - """ - - # Return False in case a public key is provided - if value.startswith('0x'): - return False - - try: - ss58_decode(value, valid_ss58_format=valid_ss58_format) - except ValueError: - return False - - return True - - -def get_ss58_format(ss58_address: str) -> int: - """ - Returns the SS58 format for given SS58 address - - Parameters - ---------- - ss58_address - - Returns - ------- - int - """ - address_decoded = base58.b58decode(ss58_address) - - if address_decoded[0] & 0b0100_0000: - ss58_format = ((address_decoded[0] & 0b0011_1111) << 2) | (address_decoded[1] >> 6) | \ - ((address_decoded[1] & 0b0011_1111) << 8) - else: - ss58_format = address_decoded[0] - - if ss58_format in [46, 47]: - raise ValueError(f"{ss58_format} is a reserved SS58 format") - - return ss58_format diff --git a/test/test_array.py b/test/test_array.py new file mode 100644 index 0000000..7014d4f --- /dev/null +++ b/test/test_array.py @@ -0,0 +1,82 @@ +# Python SCALE Codec Library +# +# Copyright 2018-2024 Stichting Polkascan (Polkascan Foundation). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import unittest + +from scalecodec.base import ScaleBytes +from scalecodec.exceptions import ScaleEncodeException +from scalecodec.types import Array, U32, U8 + + +class TestArray(unittest.TestCase): + + def test_dynamic_fixed_array_type_decode(self): + obj = Array(U32, 1).new() + self.assertEqual([1], obj.decode(ScaleBytes("0x01000000"))) + + obj = Array(U32, 3).new() + self.assertEqual([1, 2, 3], obj.decode(ScaleBytes("0x010000000200000003000000"))) + + obj = Array(U32, 0).new() + self.assertEqual([], obj.decode(ScaleBytes(bytes()))) + + def test_dynamic_fixed_array_type_decode_u8(self): + obj = Array(U8, 65).new() + obj.decode(ScaleBytes( + "0xc42b82d02bce3202f6a05d4b06d1ad46963d3be36fd0528bbe90e7f7a4e5fcd38d14234b1c9fcee920d76cfcf43b4ed5dd718e357c2bc1aae3a642975207e67f01" + )) + self.assertEqual( + "0xc42b82d02bce3202f6a05d4b06d1ad46963d3be36fd0528bbe90e7f7a4e5fcd38d14234b1c9fcee920d76cfcf43b4ed5dd718e357c2bc1aae3a642975207e67f01", + obj.value + ) + + def test_array_type_encode_u8(self): + obj = Array(U8, 2).new() + self.assertEqual('0x0102', str(obj.encode('0x0102'))) + self.assertEqual('0x0102', str(obj.encode(b'\x01\x02'))) + self.assertEqual('0x0102', str(obj.encode([1, 2]))) + + def test_array_type_encode(self): + obj = Array(U32, 2).new() + self.assertEqual('0x0100000002000000', str(obj.encode([1, 2]))) + + obj = Array(U8, 3).new() + self.assertEqual('0x010203', str(obj.encode('0x010203'))) + + def test_invalid_array_encode(self): + obj = Array(U8, 3).new() + self.assertRaises(ScaleEncodeException, obj.encode, '0x0102') + + obj = Array(U32, 3).new() + self.assertRaises(ScaleEncodeException, obj.encode, '0x0102') + + def test_array_u8(self): + obj = Array(U8, 4).new() + + value = [1, 2, 3, 4] + data = obj.encode(value) + + self.assertEqual(data, ScaleBytes('0x01020304')) + + data.reset() + self.assertEqual('0x01020304', obj.decode(data)) + + self.assertEqual(obj.value_object, b'\x01\x02\x03\x04') + + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_bitvec.py b/test/test_bitvec.py new file mode 100644 index 0000000..2c92e3e --- /dev/null +++ b/test/test_bitvec.py @@ -0,0 +1,108 @@ +# Python SCALE Codec Library +# +# Copyright 2018-2024 Stichting Polkascan (Polkascan Foundation). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import unittest + +from scalecodec.base import ScaleBytes +from scalecodec.types import BitVec + + +class TestBitvec(unittest.TestCase): + + def test_bitvec_decode(self): + obj = BitVec().new() + obj.decode(ScaleBytes('0x0c07')) + self.assertEqual(obj.value, '0b111') + + def test_bitvec_decode_size2(self): + obj = BitVec().new() + obj.decode(ScaleBytes('0x0803')) + self.assertEqual(obj.value, '0b11') + + def test_bitvec_decode_size_2bytes(self): + obj = BitVec().new() + obj.decode(ScaleBytes('0x28fd02')) + self.assertEqual(obj.value, '0b1011111101') + + def test_bitvec_encode_list(self): + obj = BitVec().new() + data = obj.encode([True, True, True]) + self.assertEqual(data.to_hex(), '0x0c07') + + def test_bitvec_encode_list2(self): + obj = BitVec().new() + data = obj.encode([True, False]) + self.assertEqual(data.to_hex(), '0x0802') + + def test_bitvec_encode_list3(self): + obj = BitVec().new() + data = obj.encode([False, True]) + self.assertEqual(data.to_hex(), '0x0401') + + def test_bitvec_encode_list4(self): + obj = BitVec().new() + data = obj.encode([True, False, False, True, True, True, True, True, False, True]) + self.assertEqual(data.to_hex(), '0x287d02') + + def test_bitvec_encode_bin_str(self): + obj = BitVec().new() + data = obj.encode('0b00000111') + self.assertEqual(data.to_hex(), '0x0c07') + + def test_bitvec_encode_bin_str2(self): + obj = BitVec().new() + data = obj.encode('0b00000010') + self.assertEqual(data.to_hex(), '0x0802') + + def test_bitvec_encode_bin_str3(self): + obj = BitVec().new() + data = obj.encode('0b00000001') + self.assertEqual(data.to_hex(), '0x0401') + + def test_bitvec_encode_bin_str4(self): + obj = BitVec().new() + data = obj.encode('0b00000010_01111101') + self.assertEqual(data.to_hex(), '0x287d02') + + def test_bitvec_encode_int(self): + obj = BitVec().new() + data = obj.encode(0b00000111) + self.assertEqual(data.to_hex(), '0x0c07') + + def test_bitvec_encode_int2(self): + obj = BitVec().new() + data = obj.encode(0b00000010) + self.assertEqual(data.to_hex(), '0x0802') + + def test_bitvec_encode_int3(self): + obj = BitVec().new() + data = obj.encode(0b00000001) + self.assertEqual(data.to_hex(), '0x0401') + + def test_bitvec_encode_int4(self): + obj = BitVec().new() + data = obj.encode(0b00000010_01111101) + self.assertEqual(data.to_hex(), '0x287d02') + + def test_bitvec_encode_empty_list(self): + obj = BitVec().new() + data = obj.encode([]) + self.assertEqual(data.to_hex(), '0x00') + + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_boolean.py b/test/test_boolean.py new file mode 100644 index 0000000..9deec2e --- /dev/null +++ b/test/test_boolean.py @@ -0,0 +1,52 @@ +# Python SCALE Codec Library +# +# Copyright 2018-2024 Stichting Polkascan (Polkascan Foundation). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import unittest + +from scalecodec.base import ScaleBytes +from scalecodec.exceptions import ScaleDecodeException +from scalecodec.types import Bool + + +class TestBoolean(unittest.TestCase): + + def test_bool_true(self): + obj = Bool().new() + obj.decode(ScaleBytes("0x01")) + self.assertEqual(obj.value, True) + + def test_bool_false(self): + obj = Bool().new() + obj.decode(ScaleBytes("0x00")) + self.assertEqual(obj.value, False) + + def test_bool_invalid(self): + obj = Bool().new() + self.assertRaises(ScaleDecodeException, obj.decode, ScaleBytes("0x02")) + + def test_bool_encode_decode(self): + scale_obj = Bool().new() + value = True + + data = scale_obj.encode(value) + scale_obj.decode(data) + + self.assertEqual(value, scale_obj.value) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_bytes.py b/test/test_bytes.py new file mode 100644 index 0000000..ff90c8c --- /dev/null +++ b/test/test_bytes.py @@ -0,0 +1,71 @@ +# Python SCALE Codec Library +# +# Copyright 2018-2024 Stichting Polkascan (Polkascan Foundation). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import unittest + +from scalecodec.base import ScaleBytes +from scalecodec.types import String, Bytes + + +class TestBytes(unittest.TestCase): + + def test_bytes_encode_decode(self): + scale_obj = Bytes.new() + value = "0x1274657374" + + data = scale_obj.encode(value) + scale_obj.decode(data) + + self.assertEqual(value, scale_obj.value) + + def test_bytes_encode_bytes(self): + value = b'This is a test' + + obj = Bytes.new() + data = obj.encode(value) + + self.assertEqual("0x385468697320697320612074657374", data.to_hex()) + + def test_bytes_encode_bytearray(self): + value = bytearray(b'This is a test') + + obj = Bytes.new() + data = obj.encode(value) + + self.assertEqual("0x385468697320697320612074657374", data.to_hex()) + + def test_bytes_encode_list_of_u8(self): + value = [84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 116, 101, 115, 116] + + obj = Bytes.new() + data = obj.encode(value) + + self.assertEqual("0x385468697320697320612074657374", data.to_hex()) + + def test_hexbytes_encode_decode(self): + + value = '0x5468697320697320612074657374' + + obj = Bytes.new() + data = obj.encode(value) + + obj_check = Bytes.new() + + self.assertEqual(obj_check.decode(data), value) + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_compact.py b/test/test_compact.py new file mode 100644 index 0000000..76d50ed --- /dev/null +++ b/test/test_compact.py @@ -0,0 +1,123 @@ +# Python SCALE Codec Library +# +# Copyright 2018-2024 Stichting Polkascan (Polkascan Foundation). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import unittest + +from scalecodec.base import ScaleBytes +from scalecodec.exceptions import ScaleDecodeException, RemainingScaleBytesNotEmptyException +from scalecodec.types import Compact, U32, U128 + + +class TestCompact(unittest.TestCase): + + def test_compact_u32(self): + obj = Compact(U32).new() + obj.decode(ScaleBytes("0x02093d00")) + self.assertEqual(obj.value, 1000000) + + def test_compact_u32_1byte(self): + obj = Compact(U32).new() + obj.decode(ScaleBytes("0x18")) + self.assertEqual(obj.value, 6) + + def test_compact_u32_remaining_bytes(self): + obj = Compact(U32).new() + with self.assertRaises(ScaleDecodeException): + obj.decode(ScaleBytes("0x02093d0001"), check_remaining=True) + + def test_compact_u32_invalid(self): + obj = Compact(U32).new() + self.assertRaises(RemainingScaleBytesNotEmptyException, obj.decode, ScaleBytes("0x")) + + def test_compact_u32_1byte_encode(self): + obj = Compact(U32).new() + obj.decode(ScaleBytes("0x18")) + + obj = Compact(U32).new() + obj.encode(6) + self.assertEqual(str(obj.data), "0x18") + + def test_compact_u32_2bytes_encode(self): + obj = Compact(U32).new() + obj.encode(6000) + self.assertEqual(str(obj.data), "0xc15d") + + def test_compact_u32_4bytes_encode(self): + + obj = Compact(U32).new() + obj.encode(1000000) + self.assertEqual(str(obj.data), "0x02093d00") + + def test_compact_u32_larger_than_4bytes_encode(self): + + obj = Compact(U32).new() + obj.encode(150000000000000) + self.assertEqual(str(obj.data), "0x0b0060b7986c88") + + def test_compact_u32_encode_decode(self): + + value = 2000001 + + obj = Compact(U32).new() + data = obj.encode(value) + + obj = Compact(U32).new() + + self.assertEqual(obj.decode(data), value) + + def test_compact_u32_encode_decode_large(self): + + value = 2**30 + + obj = Compact(U32).new() + data = obj.encode(value) + + obj = Compact(U32).new() + + self.assertEqual(obj.decode(data), value) + + def test_compact_balance_encode_decode(self): + scale_data = ScaleBytes('0x070010a5d4e8') + value = 1000000000000 + + Balance = U128 + + obj = Compact(Balance).new() + data = obj.encode(value) + + self.assertEqual(str(scale_data), str(data)) + + self.assertEqual(obj.decode(data), value) + + def test_balance(self): + Balance = U128 + obj = Compact(Balance).new() + obj.decode(ScaleBytes("0x130080cd103d71bc22")) + self.assertEqual(obj.value, 2503000000000000000) + + def test_compact_no_type(self): + data = ScaleBytes("0x02093d00") + compact = Compact().new() + compact.decode(data) + self.assertEqual(compact.value, 1000000) + + compact.encode(1000000) + self.assertEqual(compact.data, data) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_enum.py b/test/test_enum.py new file mode 100644 index 0000000..4e88ef7 --- /dev/null +++ b/test/test_enum.py @@ -0,0 +1,50 @@ +# Python SCALE Codec Library +# +# Copyright 2018-2024 Stichting Polkascan (Polkascan Foundation). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import unittest + +from scalecodec.types import Enum, Bool, U32 + + +class TestEnum(unittest.TestCase): + + def test_enum(self): + scale_obj = Enum(Bool=Bool(), Number=U32, None_=None).new() + value = {'Bool': True} + + data = scale_obj.encode(value) + scale_obj.decode(data) + + self.assertEqual(value, scale_obj.value) + + value = {'Number': 7643} + + data = scale_obj.encode(value) + scale_obj.decode(data) + + self.assertEqual(value, scale_obj.value) + + value = 'None' + + data = scale_obj.encode(value) + scale_obj.decode(data) + + self.assertEqual(value, scale_obj.value) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_float.py b/test/test_float.py new file mode 100644 index 0000000..7fd6ded --- /dev/null +++ b/test/test_float.py @@ -0,0 +1,53 @@ +# Python SCALE Codec Library +# +# Copyright 2018-2024 Stichting Polkascan (Polkascan Foundation). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +from scalecodec.base import ScaleBytes +from scalecodec.types import F64, F32 + + +class TestFloat(unittest.TestCase): + + def test_f64_decode(self): + obj = F64.new() + obj.decode(ScaleBytes("0x333333333333f33f")) + self.assertAlmostEqual(obj.value, 1.2) + + def test_f32_decode(self): + obj = F32.new() + obj.decode(ScaleBytes("0x9a99993f")) + self.assertAlmostEqual(obj.value, 1.2) + + def test_f64_encode(self): + obj = F64.new() + obj.encode(-0.0) + self.assertEqual(str(obj.data), "0x0000000000000080") + + def test_f64_encode_invalid_input(self): + obj = F64.new() + with self.assertRaises(ValueError) as cm: + obj.encode(-0) + self.assertEqual('0 is not a float', str(cm.exception)) + + def test_f32_encode(self): + obj = F32.new() + obj.encode(-0.0) + self.assertEqual(str(obj.data), "0x00000080") + + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_hashmap.py b/test/test_hashmap.py new file mode 100644 index 0000000..76b1575 --- /dev/null +++ b/test/test_hashmap.py @@ -0,0 +1,38 @@ +# Python SCALE Codec Library +# +# Copyright 2018-2024 Stichting Polkascan (Polkascan Foundation). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import unittest + +from scalecodec.base import ScaleBytes +from scalecodec.types import String, HashMap, U32 + + +class TestHashmap(unittest.TestCase): + + def test_hashmap_encode(self): + obj = HashMap(String, U32).new() + data = obj.encode([('1', 2), ('23', 24), ('28', 30), ('45', 80)]) + self.assertEqual(data.to_hex(), '0x10043102000000083233180000000832381e00000008343550000000') + + def test_hashmap_decode(self): + obj = HashMap(String, U32).new() + data = ScaleBytes("0x10043102000000083233180000000832381e00000008343550000000") + self.assertEqual([('1', 2), ('23', 24), ('28', 30), ('45', 80)], obj.decode(data)) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_integer.py b/test/test_integer.py new file mode 100644 index 0000000..d783130 --- /dev/null +++ b/test/test_integer.py @@ -0,0 +1,60 @@ +# Python SCALE Codec Library +# +# Copyright 2018-2024 Stichting Polkascan (Polkascan Foundation). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import unittest + +from scalecodec.base import ScaleBytes +from scalecodec.types import U16, I16, U8 + + +class TestInteger(unittest.TestCase): + + def test_u16_decode(self): + obj = U16.new() + obj.decode(ScaleBytes("0x2efb")) + self.assertEqual(obj.value, 64302) + + def test_i16_decode(self): + obj = I16.new() + obj.decode(ScaleBytes("0x2efb")) + self.assertEqual(obj.value, -1234) + + def test_u16(self): + obj = U16.new() + obj.encode(64302) + self.assertEqual(str(obj.data), "0x2efb") + + def test_i16_encode(self): + obj = I16.new() + obj.encode(-1234) + self.assertEqual(str(obj.data), "0x2efb") + + def test_i16_encode_out_of_bounds(self): + obj = I16.new() + self.assertRaises(OverflowError, obj.encode, -32769) + + def test_u8(self): + scale_obj = U8.new() + value = 42 + + data = scale_obj.encode(value) + scale_obj.decode(data) + + self.assertEqual(value, scale_obj.value) + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_option.py b/test/test_option.py new file mode 100644 index 0000000..e20e38e --- /dev/null +++ b/test/test_option.py @@ -0,0 +1,61 @@ +# Python SCALE Codec Library +# +# Copyright 2018-2024 Stichting Polkascan (Polkascan Foundation). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import unittest + +from scalecodec.types import Option, U16, Bytes, String + + +class TestOption(unittest.TestCase): + + def test_option(self): + scale_obj = Option(U16).new() + + value = None + + data = scale_obj.encode(value) + scale_obj.decode(data) + + self.assertEqual(value, scale_obj.value) + + value = 12788 + + data = scale_obj.encode(value) + scale_obj.decode(data) + + self.assertEqual(value, scale_obj.value) + + def test_option_empty_encode_decode(self): + + value = None + + obj = Option(Bytes).new() + data = obj.encode(value) + + self.assertEqual(obj.decode(data), value) + + def test_option_string_encode_decode(self): + value = "Test" + + obj = Option(String).new() + data = obj.encode(value) + + self.assertEqual(obj.decode(data), value) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_scale_primitives.py b/test/test_scale_primitives.py deleted file mode 100644 index 1d722ce..0000000 --- a/test/test_scale_primitives.py +++ /dev/null @@ -1,175 +0,0 @@ -# Python SCALE Codec Library -# -# Copyright 2018-2023 Stichting Polkascan (Polkascan Foundation). -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import unittest - -from scalecodec.base import ScaleBytes -from scalecodec.types import Struct, U8, Tuple, U16, Enum, U32, Bool, Option, Compact, Vec, Array, Bytes, String - - -class TestScaleTypes(unittest.TestCase): - - metadata_fixture_dict = {} - metadata_decoder = None - runtime_config_v14 = None - metadata_v14_obj = None - - def test_u8(self): - scale_obj = U8.new() - value = 42 - - data = scale_obj.encode(value) - scale_obj.decode(data) - - self.assertEqual(value, scale_obj.value) - - def test_u16(self): - scale_obj = U16.new() - value = 64302 - - data = scale_obj.encode(value) - self.assertEqual(data, ScaleBytes("0x2efb")) - scale_obj.decode(data) - - self.assertEqual(value, scale_obj.value) - - def test_bool(self): - scale_obj = Bool().new() - value = True - - data = scale_obj.encode(value) - scale_obj.decode(data) - - self.assertEqual(value, scale_obj.value) - - def test_tuple(self): - scale_obj = Tuple(U8, U8).new() - value = (1, 5) - - data = scale_obj.encode(value) - scale_obj.decode(data) - - self.assertEqual(value, scale_obj.value) - - def test_struct(self): - scale_obj = Struct(test=U8, test2=Tuple(U8, U8)).new() - value = {'test': 2, "test2": (1, 5)} - - data = scale_obj.encode(value) - scale_obj.decode(data) - - self.assertEqual(value, scale_obj.value) - - def test_enum(self): - scale_obj = Enum(Bool=Bool(), Number=U32, None_=None).new() - value = {'Bool': True} - - data = scale_obj.encode(value) - scale_obj.decode(data) - - self.assertEqual(value, scale_obj.value) - - value = {'Number': 7643} - - data = scale_obj.encode(value) - scale_obj.decode(data) - - self.assertEqual(value, scale_obj.value) - - value = 'None' - - data = scale_obj.encode(value) - scale_obj.decode(data) - - self.assertEqual(value, scale_obj.value) - - def test_option(self): - scale_obj = Option(U16).new() - - value = None - - data = scale_obj.encode(value) - scale_obj.decode(data) - - self.assertEqual(value, scale_obj.value) - - value = 12788 - - data = scale_obj.encode(value) - scale_obj.decode(data) - - self.assertEqual(value, scale_obj.value) - - def test_compact(self): - data = ScaleBytes("0x02093d00") - compact = Compact().new() - compact.decode(data) - self.assertEqual(compact.value, 1000000) - - compact.encode(1000000) - self.assertEqual(compact.data, data) - - def test_vec(self): - - obj = Vec(U16).new() - - value = [1, 2] - data = obj.encode(value) - - self.assertEqual(data, ScaleBytes('0x0801000200')) - self.assertEqual(value, obj.decode(data)) - - self.assertEqual(obj.value_object[0].value, 1) - self.assertEqual(obj.value_object[1].value, 2) - - def test_array(self): - obj = Array(U8, 4).new() - - value = [1, 2, 3, 4] - data = obj.encode(value) - - self.assertEqual(data, ScaleBytes('0x01020304')) - - data.reset() - self.assertEqual('0x01020304', obj.decode(data)) - - self.assertEqual(obj.value_object, b'\x01\x02\x03\x04') - - def test_bytes(self): - scale_obj = Bytes.new() - value = "0x1274657374" - - data = scale_obj.encode(value) - scale_obj.decode(data) - - self.assertEqual(value, scale_obj.value) - - def test_string(self): - scale_obj = String.new() - value = "test" - - data = scale_obj.encode(value) - scale_obj.decode(data) - - self.assertEqual(value, scale_obj.value) - - def test_string_multibyte_chars(self): - obj = String.new() - - data = obj.encode('µ') - self.assertEqual('0x08c2b5', data.to_hex()) - - obj.decode(data) - self.assertEqual(obj.value, "µ") diff --git a/test/test_scale_types.py b/test/test_scale_types.py index 68c2c27..b9f390c 100644 --- a/test/test_scale_types.py +++ b/test/test_scale_types.py @@ -1,6 +1,6 @@ # Python SCALE Codec Library # -# Copyright 2018-2020 Stichting Polkascan (Polkascan Foundation). +# Copyright 2018-2024 Stichting Polkascan (Polkascan Foundation). # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,20 +17,11 @@ import unittest from scalecodec.base import ScaleBytes -from scalecodec.exceptions import RemainingScaleBytesNotEmptyException, ScaleEncodeException, ScaleDecodeException - -from scalecodec.types import (Compact, U32, U16, I16, Tuple, String, Vec, BitVec, Bool, Array, HashMap, U8, - F32, F64, UnsignedInteger) -from scalecodec.utils.ss58 import ss58_encode_account_index +from scalecodec.types import Compact, U32, U16, Tuple class TestScaleTypes(unittest.TestCase): - metadata_fixture_dict = {} - metadata_decoder = None - runtime_config_v14 = None - metadata_v14_obj = None - def test_multiple_decode_without_error(self): obj = U16.new() obj.decode(ScaleBytes("0x2efb")) @@ -54,226 +45,3 @@ def test_value_object_shorthand(self): obj.decode(ScaleBytes("0x0c00")) self.assertEqual(obj[0], 3) self.assertEqual(obj[1], 0) - - def test_compact_u32(self): - obj = Compact(U32).new() - obj.decode(ScaleBytes("0x02093d00")) - self.assertEqual(obj.value, 1000000) - - def test_compact_u32_1byte(self): - obj = Compact(U32).new() - obj.decode(ScaleBytes("0x18")) - self.assertEqual(obj.value, 6) - - def test_compact_u32_remaining_bytes(self): - obj = Compact(U32).new() - with self.assertRaises(ScaleDecodeException): - obj.decode(ScaleBytes("0x02093d0001"), check_remaining=True) - - def test_compact_u32_invalid(self): - obj = Compact(U32).new() - self.assertRaises(RemainingScaleBytesNotEmptyException, obj.decode, ScaleBytes("0x")) - - def test_u16(self): - obj = U16.new() - obj.decode(ScaleBytes("0x2efb")) - self.assertEqual(obj.value, 64302) - - def test_i16(self): - obj = I16.new() - obj.decode(ScaleBytes("0x2efb")) - self.assertEqual(obj.value, -1234) - - def test_f64(self): - obj = F64.new() - obj.decode(ScaleBytes("0x333333333333f33f")) - self.assertAlmostEqual(obj.value, 1.2) - - def test_f32(self): - obj = F32.new() - obj.decode(ScaleBytes("0x9a99993f")) - self.assertAlmostEqual(obj.value, 1.2) - - def test_bool_true(self): - obj = Bool().new() - obj.decode(ScaleBytes("0x01")) - self.assertEqual(obj.value, True) - - def test_bool_false(self): - obj = Bool().new() - obj.decode(ScaleBytes("0x00")) - self.assertEqual(obj.value, False) - - def test_bool_invalid(self): - obj = Bool().new() - self.assertRaises(ScaleDecodeException, obj.decode, ScaleBytes("0x02")) - - def test_string(self): - obj = String.new() - obj.decode(ScaleBytes("0x1054657374")) - self.assertEqual(obj.value, "Test") - - data = obj.encode("Test") - - self.assertEqual("0x1054657374", data.to_hex()) - - def test_string_multibyte_chars(self): - obj = String.new() - - data = obj.encode('µ') - self.assertEqual('0x08c2b5', data.to_hex()) - - obj.decode(data) - self.assertEqual(obj.value, "µ") - - def test_tuple(self): - obj = Tuple(Compact(U32), Compact(U32)).new() - obj.decode(ScaleBytes("0x0c00")) - self.assertEqual(obj.value, (3, 0)) - - def test_tuple_deserialize(self): - obj = Tuple(Compact(U32), Compact(U32)).new() - obj.deserialize((3, 2)) - self.assertEqual(obj.value, (3, 2)) - - def test_balance(self): - Balance = UnsignedInteger(128) - obj = Compact(Balance).new() - obj.decode(ScaleBytes("0x130080cd103d71bc22")) - self.assertEqual(obj.value, 2503000000000000000) - - def test_dynamic_fixed_array_type_decode(self): - obj = Array(U32, 1).new() - self.assertEqual([1], obj.decode(ScaleBytes("0x01000000"))) - - obj = Array(U32, 3).new() - self.assertEqual([1, 2, 3], obj.decode(ScaleBytes("0x010000000200000003000000"))) - - obj = Array(U32, 0).new() - self.assertEqual([], obj.decode(ScaleBytes(bytes()))) - - def test_dynamic_fixed_array_type_decode_u8(self): - obj = Array(U8, 65).new() - obj.decode(ScaleBytes( - "0xc42b82d02bce3202f6a05d4b06d1ad46963d3be36fd0528bbe90e7f7a4e5fcd38d14234b1c9fcee920d76cfcf43b4ed5dd718e357c2bc1aae3a642975207e67f01" - )) - self.assertEqual( - "0xc42b82d02bce3202f6a05d4b06d1ad46963d3be36fd0528bbe90e7f7a4e5fcd38d14234b1c9fcee920d76cfcf43b4ed5dd718e357c2bc1aae3a642975207e67f01", - obj.value - ) - - def test_dynamic_fixed_array_type_encode_u8(self): - obj = Array(U8, 2).new() - self.assertEqual('0x0102', str(obj.encode('0x0102'))) - self.assertEqual('0x0102', str(obj.encode(b'\x01\x02'))) - self.assertEqual('0x0102', str(obj.encode([1, 2]))) - - def test_dynamic_fixed_array_type_encode(self): - obj = Array(U32, 2).new() - self.assertEqual('0x0100000002000000', str(obj.encode([1, 2]))) - - obj = Array(U8, 3).new() - self.assertEqual('0x010203', str(obj.encode('0x010203'))) - - def test_invalid_fixed_array_type_encode(self): - obj = Array(U8, 3).new() - self.assertRaises(ScaleEncodeException, obj.encode, '0x0102') - - obj = Array(U32, 3).new() - self.assertRaises(ScaleEncodeException, obj.encode, '0x0102') - - - - def test_ss58_encode_index(self): - self.assertEqual(ss58_encode_account_index(0), 'F7Hs') - - def test_bitvec_decode(self): - obj = BitVec().new() - obj.decode(ScaleBytes('0x0c07')) - self.assertEqual(obj.value, '0b111') - - def test_bitvec_decode_size2(self): - obj = BitVec().new() - obj.decode(ScaleBytes('0x0803')) - self.assertEqual(obj.value, '0b11') - - def test_bitvec_decode_size_2bytes(self): - obj = BitVec().new() - obj.decode(ScaleBytes('0x28fd02')) - self.assertEqual(obj.value, '0b1011111101') - - def test_bitvec_encode_list(self): - obj = BitVec().new() - data = obj.encode([True, True, True]) - self.assertEqual(data.to_hex(), '0x0c07') - - def test_bitvec_encode_list2(self): - obj = BitVec().new() - data = obj.encode([True, False]) - self.assertEqual(data.to_hex(), '0x0802') - - def test_bitvec_encode_list3(self): - obj = BitVec().new() - data = obj.encode([False, True]) - self.assertEqual(data.to_hex(), '0x0401') - - def test_bitvec_encode_list4(self): - obj = BitVec().new() - data = obj.encode([True, False, False, True, True, True, True, True, False, True]) - self.assertEqual(data.to_hex(), '0x287d02') - - def test_bitvec_encode_bin_str(self): - obj = BitVec().new() - data = obj.encode('0b00000111') - self.assertEqual(data.to_hex(), '0x0c07') - - def test_bitvec_encode_bin_str2(self): - obj = BitVec().new() - data = obj.encode('0b00000010') - self.assertEqual(data.to_hex(), '0x0802') - - def test_bitvec_encode_bin_str3(self): - obj = BitVec().new() - data = obj.encode('0b00000001') - self.assertEqual(data.to_hex(), '0x0401') - - def test_bitvec_encode_bin_str4(self): - obj = BitVec().new() - data = obj.encode('0b00000010_01111101') - self.assertEqual(data.to_hex(), '0x287d02') - - def test_bitvec_encode_int(self): - obj = BitVec().new() - data = obj.encode(0b00000111) - self.assertEqual(data.to_hex(), '0x0c07') - - def test_bitvec_encode_int2(self): - obj = BitVec().new() - data = obj.encode(0b00000010) - self.assertEqual(data.to_hex(), '0x0802') - - def test_bitvec_encode_int3(self): - obj = BitVec().new() - data = obj.encode(0b00000001) - self.assertEqual(data.to_hex(), '0x0401') - - def test_bitvec_encode_int4(self): - obj = BitVec().new() - data = obj.encode(0b00000010_01111101) - self.assertEqual(data.to_hex(), '0x287d02') - - def test_bitvec_encode_empty_list(self): - obj = BitVec().new() - data = obj.encode([]) - self.assertEqual(data.to_hex(), '0x00') - - def test_hashmap_encode(self): - obj = HashMap(String, U32).new() - data = obj.encode([('1', 2), ('23', 24), ('28', 30), ('45', 80)]) - self.assertEqual(data.to_hex(), '0x10043102000000083233180000000832381e00000008343550000000') - - def test_hashmap_decode(self): - obj = HashMap(String, U32).new() - data = ScaleBytes("0x10043102000000083233180000000832381e00000008343550000000") - self.assertEqual([('1', 2), ('23', 24), ('28', 30), ('45', 80)], obj.decode(data)) - diff --git a/test/test_ss58.py b/test/test_ss58.py deleted file mode 100644 index 290f6b2..0000000 --- a/test/test_ss58.py +++ /dev/null @@ -1,216 +0,0 @@ -# Python SCALE Codec Library -# -# Copyright 2018-2021 Stichting Polkascan (Polkascan Foundation). -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest - -from scalecodec.utils.ss58 import ss58_decode, ss58_encode, ss58_encode_account_index, ss58_decode_account_index, \ - is_valid_ss58_address - - -class SS58TestCase(unittest.TestCase): - - @classmethod - def setUpClass(cls) -> None: - - cls.alice_keypair = { - 'address': '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', - 'public_key': '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d', - 'ss58_format': 42 - } - - cls.subkey_pairs = [ - { - 'address': '5EU9mjvZdLRGyDFiBHjxrxvQuaaBpeTZCguhxM3yMX8cpZ2u', - 'public_key': '0x6a5a5957ce778c174c02c151e7c4917ac127b33ad8485f579f830fc15d31bc5a', - 'ss58_format': 42 - }, - { - # ecdsa - 'address': '4pbsSkWcBaYoFHrKJZp5fDVUKbqSYD9dhZZGvpp3vQ5ysVs5ybV', - 'public_key': '0x035676109c54b9a16d271abeb4954316a40a32bcce023ac14c8e26e958aa68fba9', - 'ss58_format': 200 - }, - { - 'address': 'yGF4JP7q5AK46d1FPCEm9sYQ4KooSjHMpyVAjLnsCSWVafPnf', - 'public_key': '0x66cd6cf085627d6c85af1aaf2bd10cf843033e929b4e3b1c2ba8e4aa46fe111b', - 'ss58_format': 255 - }, - { - 'address': 'yGDYxQatQwuxqT39Zs4LtcTnpzE12vXb7ZJ6xpdiHv6gTu1hF', - 'public_key': '0x242fd5a078ac6b7c3c2531e9bcf1314343782aeb58e7bc6880794589e701db55', - 'ss58_format': 255 - }, - { - 'address': 'mHm8k9Emsvyfp3piCauSH684iA6NakctF8dySQcX94GDdrJrE', - 'public_key': '0x44d5a3ac156335ea99d33a83c57c7146c40c8e2260a8a4adf4e7a86256454651', - 'ss58_format': 4242 - }, - { - 'address': 'r6Gr4gaMP8TsjhFbqvZhv3YvnasugLiRJpzpRHifsqqG18UXa', - 'public_key': '0x88f01441682a17b52d6ae12d1a5670cf675fd254897efabaa5069eb3a701ab73', - 'ss58_format': 14269 - } - ] - - def test_encode_key_pair_alice_address(self): - self.assertEqual(self.alice_keypair['address'], "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY") - - def test_encode_1_byte_account_index(self): - self.assertEqual('F7NZ', ss58_encode_account_index(1)) - - def test_encode_1_byte_account_index_with_format(self): - self.assertEqual('g4b', ss58_encode_account_index(1, ss58_format=2)) - self.assertEqual('g4b', ss58_encode('0x01', ss58_format=2)) - - def test_encode_2_bytes_account_index(self): - self.assertEqual('3xygo', ss58_encode_account_index(256, ss58_format=2)) - self.assertEqual('3xygo', ss58_encode('0x0001', ss58_format=2)) - - def test_encode_4_bytes_account_index(self): - self.assertEqual('zswfoZa', ss58_encode_account_index(67305985, ss58_format=2)) - self.assertEqual('zswfoZa', ss58_encode('0x01020304', ss58_format=2)) - - def test_encode_8_bytes_account_index(self): - self.assertEqual('848Gh2GcGaZia', ss58_encode('0x2a2c0a0000000000', ss58_format=2)) - - def test_decode_1_byte_account_index(self): - self.assertEqual(1, ss58_decode_account_index('F7NZ')) - - def test_decode_2_bytes_account_index(self): - self.assertEqual(256, ss58_decode_account_index('3xygo')) - - def test_decode_4_bytes_account_index(self): - self.assertEqual(67305985, ss58_decode_account_index('zswfoZa')) - - def test_decode_8_bytes_account_index(self): - self.assertEqual(666666, ss58_decode_account_index('848Gh2GcGaZia')) - - def test_encode_33_byte_address(self): - self.assertEqual( - 'KWCv1L3QX9LDPwY4VzvLmarEmXjVJidUzZcinvVnmxAJJCBou', - ss58_encode('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077') - ) - - def test_encode_with_2_byte_prefix(self): - public_key = ss58_decode('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua') - - self.assertEqual( - 'yGHU8YKprxHbHdEv7oUK4rzMZXtsdhcXVG2CAMyC9WhzhjH2k', - ss58_encode(public_key, ss58_format=255) - ) - - def test_encode_subkey_generated_pairs(self): - for subkey_pair in self.subkey_pairs: - self.assertEqual( - subkey_pair['address'], - ss58_encode(address=subkey_pair['public_key'], ss58_format=subkey_pair['ss58_format']) - ) - - def test_decode_subkey_generated_pairs(self): - for subkey_pair in self.subkey_pairs: - self.assertEqual( - subkey_pair['public_key'], - '0x' + ss58_decode(address=subkey_pair['address'], valid_ss58_format=subkey_pair['ss58_format']) - ) - - def test_invalid_ss58_format_range_exceptions(self): - with self.assertRaises(ValueError) as cm: - ss58_encode(self.alice_keypair['public_key'], ss58_format=-1) - self.assertEqual('Invalid value for ss58_format', str(cm.exception)) - - with self.assertRaises(ValueError) as cm: - ss58_encode(self.alice_keypair['public_key'], ss58_format=16384) - - self.assertEqual('Invalid value for ss58_format', str(cm.exception)) - - def test_invalid_reserved_ss58_format(self): - with self.assertRaises(ValueError) as cm: - ss58_encode(self.alice_keypair['public_key'], ss58_format=46) - - self.assertEqual('Invalid value for ss58_format', str(cm.exception)) - - with self.assertRaises(ValueError) as cm: - ss58_encode(self.alice_keypair['public_key'], ss58_format=47) - - self.assertEqual('Invalid value for ss58_format', str(cm.exception)) - - def test_invalid_public_key(self): - with self.assertRaises(ValueError) as cm: - ss58_encode(self.alice_keypair['public_key'][:30]) - - self.assertEqual('Invalid length for address', str(cm.exception)) - - def test_decode_public_key(self): - self.assertEqual( - '0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077', - ss58_decode('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077') - ) - - def test_decode_reserved_ss58_formats(self): - - with self.assertRaises(ValueError) as cm: - ss58_decode('MGP3U1wqNhFofseKXU7B6FcZuLbvQvJFyin1EvQM65mBcNsY8') - - self.assertEqual('46 is a reserved SS58 format', str(cm.exception)) - - with self.assertRaises(ValueError) as cm: - ss58_decode('MhvaLBvSb5jhjrftHLQPAvJegnpXgyDTE1ZprRNzAcfQSRdbL') - - self.assertEqual('47 is a reserved SS58 format', str(cm.exception)) - - def test_invalid_ss58_format_check(self): - with self.assertRaises(ValueError) as cm: - ss58_decode('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua', valid_ss58_format=2) - - self.assertEqual('Invalid SS58 format', str(cm.exception)) - - def test_decode_invalid_checksum(self): - with self.assertRaises(ValueError) as cm: - ss58_decode('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQub') - - self.assertEqual('Invalid checksum', str(cm.exception)) - - def test_decode_invalid_length(self): - with self.assertRaises(ValueError) as cm: - ss58_decode('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQubsdhfjksdhfkj') - - self.assertEqual('Invalid address length', str(cm.exception)) - - def test_decode_empty_string(self): - with self.assertRaises(ValueError) as cm: - ss58_decode('') - - self.assertEqual('Empty address provided', str(cm.exception)) - - def test_is_valid_ss58_address(self): - self.assertTrue(is_valid_ss58_address('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY')) - self.assertTrue(is_valid_ss58_address('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', valid_ss58_format=42)) - self.assertFalse(is_valid_ss58_address('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', valid_ss58_format=2)) - - self.assertTrue(is_valid_ss58_address('GLdQ4D4wkeEJUX8DBT9HkpycFVYQZ3fmJyQ5ZgBRxZ4LD3S', valid_ss58_format=2)) - self.assertFalse(is_valid_ss58_address('GLdQ4D4wkeEJUX8DBT9HkpycFVYQZ3fmJyQ5ZgBRxZ4LD3S', valid_ss58_format=42)) - self.assertFalse(is_valid_ss58_address('GLdQ4D4wkeEJUX8DBT9HkpycFVYQZ3fmJyQ5ZgBRxZ4LD3S', valid_ss58_format=0)) - self.assertTrue(is_valid_ss58_address('12gX42C4Fj1wgtfgoP624zeHrcPBqzhb4yAENyvFdGX6EUnN', valid_ss58_format=0)) - - self.assertFalse(is_valid_ss58_address('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQ')) - self.assertFalse(is_valid_ss58_address('6GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY')) - self.assertFalse(is_valid_ss58_address('0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d')) - self.assertFalse(is_valid_ss58_address('d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d')) - self.assertFalse(is_valid_ss58_address('incorrect_string')) - - -if __name__ == '__main__': - unittest.main() diff --git a/test/test_string.py b/test/test_string.py new file mode 100644 index 0000000..0730688 --- /dev/null +++ b/test/test_string.py @@ -0,0 +1,57 @@ +# Python SCALE Codec Library +# +# Copyright 2018-2024 Stichting Polkascan (Polkascan Foundation). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import unittest + +from scalecodec.base import ScaleBytes +from scalecodec.types import String + + +class TestString(unittest.TestCase): + + def test_string_decode_encode(self): + obj = String.new() + obj.decode(ScaleBytes("0x1054657374")) + self.assertEqual(obj.value, "Test") + + data = obj.encode("Test") + + self.assertEqual("0x1054657374", data.to_hex()) + + def test_string_encode_decode(self): + + value = 'This is a test' + + obj = String.new() + data = obj.encode(value) + + obj_check = String.new() + + self.assertEqual(obj_check.decode(data), value) + + def test_string_multibyte_chars(self): + obj = String.new() + + data = obj.encode('µ') + self.assertEqual('0x08c2b5', data.to_hex()) + + obj.decode(data) + self.assertEqual(obj.value, "µ") + + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_tuple.py b/test/test_tuple.py new file mode 100644 index 0000000..57fb8ec --- /dev/null +++ b/test/test_tuple.py @@ -0,0 +1,47 @@ +# Python SCALE Codec Library +# +# Copyright 2018-2024 Stichting Polkascan (Polkascan Foundation). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import unittest + +from scalecodec.base import ScaleBytes +from scalecodec.types import Tuple, Compact, U32, U8 + + +class TestTuple(unittest.TestCase): + + def test_tuple_decode(self): + obj = Tuple(Compact(U32), Compact(U32)).new() + obj.decode(ScaleBytes("0x0c00")) + self.assertEqual(obj.value, (3, 0)) + + def test_tuple_encode_decode(self): + scale_obj = Tuple(U8, U8).new() + value = (1, 5) + + data = scale_obj.encode(value) + scale_obj.decode(data) + + self.assertEqual(value, scale_obj.value) + + def test_tuple_deserialize(self): + obj = Tuple(Compact(U32), Compact(U32)).new() + obj.deserialize((3, 2)) + self.assertEqual(obj.value, (3, 2)) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_type_encoding.py b/test/test_type_encoding.py deleted file mode 100644 index a98e289..0000000 --- a/test/test_type_encoding.py +++ /dev/null @@ -1,189 +0,0 @@ -# Python SCALE Codec Library -# -# Copyright 2018-2020 Stichting Polkascan (Polkascan Foundation). -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os -import unittest - -from scalecodec.base import ScaleBytes - -from scalecodec.types import Compact, U16, I16, U32, Vec, Bytes, String, Option, F64, F32, U128 - - -class TestScaleTypeEncoding(unittest.TestCase): - - def test_u16(self): - obj = U16.new() - obj.encode(64302) - self.assertEqual(str(obj.data), "0x2efb") - - def test_i16(self): - obj = I16.new() - obj.encode(-1234) - self.assertEqual(str(obj.data), "0x2efb") - - def test_i16_out_of_bounds(self): - obj = I16.new() - self.assertRaises(OverflowError, obj.encode, -32769) - - def test_f64(self): - obj = F64.new() - obj.encode(-0.0) - self.assertEqual(str(obj.data), "0x0000000000000080") - - def test_f64_invalid_input(self): - obj = F64.new() - with self.assertRaises(ValueError) as cm: - obj.encode(-0) - self.assertEqual('0 is not a float', str(cm.exception)) - - def test_f32(self): - obj = F32.new() - obj.encode(-0.0) - self.assertEqual(str(obj.data), "0x00000080") - - def test_compact_u32_1byte(self): - obj = Compact(U32).new() - obj.decode(ScaleBytes("0x18")) - - obj = Compact(U32).new() - obj.encode(6) - self.assertEqual(str(obj.data), "0x18") - - def test_compact_u32_2bytes(self): - obj = Compact(U32).new() - obj.encode(6000) - self.assertEqual(str(obj.data), "0xc15d") - - def test_compact_u32_4bytes(self): - - obj = Compact(U32).new() - obj.encode(1000000) - self.assertEqual(str(obj.data), "0x02093d00") - - def test_compact_u32_larger_than_4bytes(self): - - obj = Compact(U32).new() - obj.encode(150000000000000) - self.assertEqual(str(obj.data), "0x0b0060b7986c88") - - def test_compact_u32_encode_decode(self): - - value = 2000001 - - obj = Compact(U32).new() - data = obj.encode(value) - - obj = Compact(U32).new() - - self.assertEqual(obj.decode(data), value) - - def test_compact_u32_encode_decode_large(self): - - value = 2**30 - - obj = Compact(U32).new() - data = obj.encode(value) - - obj = Compact(U32).new() - - self.assertEqual(obj.decode(data), value) - - def test_vec_string_encode_decode(self): - - value = ['test', 'vec'] - - obj = Vec(String).new() - data = obj.encode(value) - - obj = Vec(String).new() - - self.assertEqual(value, obj.decode(data)) - - - def test_bytes_encode_decode(self): - - value = 'This is a test' - - obj = String.new() - data = obj.encode(value) - - obj_check = String.new() - - self.assertEqual(obj_check.decode(data), value) - - def test_bytes_encode_bytes(self): - value = b'This is a test' - - obj = Bytes.new() - data = obj.encode(value) - - self.assertEqual("0x385468697320697320612074657374", data.to_hex()) - - def test_bytes_encode_bytearray(self): - value = bytearray(b'This is a test') - - obj = Bytes.new() - data = obj.encode(value) - - self.assertEqual("0x385468697320697320612074657374", data.to_hex()) - - def test_bytes_encode_list_of_u8(self): - value = [84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 116, 101, 115, 116] - - obj = Bytes.new() - data = obj.encode(value) - - self.assertEqual("0x385468697320697320612074657374", data.to_hex()) - - def test_hexbytes_encode_decode(self): - - value = '0x5468697320697320612074657374' - - obj = Bytes.new() - data = obj.encode(value) - - obj_check = Bytes.new() - - self.assertEqual(obj_check.decode(data), value) - - def test_compact_balance_encode_decode(self): - scale_data = ScaleBytes('0x070010a5d4e8') - value = 1000000000000 - - Balance = U128 - - obj = Compact(Balance).new() - data = obj.encode(value) - - self.assertEqual(str(scale_data), str(data)) - - self.assertEqual(obj.decode(data), value) - - def test_option_empty_encode_decode(self): - - value = None - - obj = Option(Bytes).new() - data = obj.encode(value) - - self.assertEqual(obj.decode(data), value) - - def test_option_string_encode_decode(self): - value = "Test" - - obj = Option(String).new() - data = obj.encode(value) - - self.assertEqual(obj.decode(data), value) diff --git a/test/test_vec.py b/test/test_vec.py new file mode 100644 index 0000000..984fe35 --- /dev/null +++ b/test/test_vec.py @@ -0,0 +1,52 @@ +# Python SCALE Codec Library +# +# Copyright 2018-2024 Stichting Polkascan (Polkascan Foundation). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import unittest + +from scalecodec.base import ScaleBytes +from scalecodec.types import Vec, String, U16 + + +class TestVec(unittest.TestCase): + + def test_vec_string_encode_decode(self): + + value = ['test', 'vec'] + + obj = Vec(String).new() + data = obj.encode(value) + + obj = Vec(String).new() + + self.assertEqual(value, obj.decode(data)) + + def test_vec_integer(self): + + obj = Vec(U16).new() + + value = [1, 2] + data = obj.encode(value) + + self.assertEqual(data, ScaleBytes('0x0801000200')) + self.assertEqual(value, obj.decode(data)) + + self.assertEqual(obj.value_object[0].value, 1) + self.assertEqual(obj.value_object[1].value, 2) + + +if __name__ == '__main__': + unittest.main()