def test_metadata_registry_v13(self): metadata_obj = self.runtime_config.create_scale_object( "MetadataVersioned", data=ScaleBytes(self.metadata_fixture_dict['V13'])) metadata_obj.decode() self.assertEqual(metadata_obj.value_object[1].index, 13) self.assertIsNone(metadata_obj.portable_registry) self.assertGreater(len(metadata_obj[1][1]['modules']), 0) self.assertGreater(len(metadata_obj.value[1]['V13']['modules']), 0) self.assertGreater(len(metadata_obj.call_index.items()), 0)
def test_enum_multiple_fields(self): obj = self.runtime_config.create_scale_object( 'sp_runtime::generic::digest::DigestItem', ScaleBytes("0x06010203041054657374")) obj.decode() self.assertEqual({'PreRuntime': ("0x01020304", "Test")}, obj.value) data = obj.encode({'PreRuntime': ("0x01020304", "Test")}) self.assertEqual("0x06010203041054657374", data.to_hex())
def process_encode(self, value): if value == '00': self.period = None self.phase = None return ScaleBytes('0x00') if isinstance(value, dict): value = self._tuple_from_dict(value) if isinstance(value, tuple) and len(value) == 2: period, phase = value if not isinstance(phase, int) or not isinstance(period, int): raise ValueError("Phase and period must be ints") if phase > period: raise ValueError("Phase must be less than period") self.period = period self.phase = phase quantize_factor = max(period >> 12, 1) encoded = min(15, max(1, trailing_zeros(period) - 1)) | ((phase // quantize_factor) << 4) return ScaleBytes(encoded.to_bytes(length=2, byteorder='little', signed=False)) raise ValueError("Value must be the string '00' or tuple of two ints")
def setUpClass(cls): module_path = os.path.dirname(__file__) cls.metadata_fixture_dict = load_type_registry_file( os.path.join(module_path, 'fixtures', 'metadata_hex.json')) RuntimeConfiguration().update_type_registry( load_type_registry_preset("metadata_types")) cls.metadata_decoder = RuntimeConfiguration().create_scale_object( 'MetadataVersioned', data=ScaleBytes(cls.metadata_fixture_dict['V10'])) cls.metadata_decoder.decode()
def test_vec_accountid(self): obj = ScaleDecoder.get_decoder_class( 'Vec<AccountId>', ScaleBytes( "0x0865d2273adeb04478658e183dc5edf41f1d86e42255442af62e72dbf1e6c0b97765d2273adeb04478658e183dc5edf41f1d86e42255442af62e72dbf1e6c0b977" )) obj.decode() self.assertListEqual(obj.value, [ '0x65d2273adeb04478658e183dc5edf41f1d86e42255442af62e72dbf1e6c0b977', '0x65d2273adeb04478658e183dc5edf41f1d86e42255442af62e72dbf1e6c0b977' ])
def test_generic_vote(self): runtime_config = RuntimeConfigurationObject(ss58_format=2) vote = runtime_config.create_scale_object('GenericVote') data = vote.encode({'aye': True, 'conviction': 'Locked2x'}) self.assertEqual('0x82', data.to_hex()) vote.decode(ScaleBytes('0x04')) self.assertEqual(vote.value, {'aye': False, 'conviction': 'Locked4x'})
def test_vec_accountid(self): obj = RuntimeConfiguration().create_scale_object( 'Vec<AccountId>', ScaleBytes( "0x0865d2273adeb04478658e183dc5edf41f1d86e42255442af62e72dbf1e6c0b97765d2273adeb04478658e183dc5edf41f1d86e42255442af62e72dbf1e6c0b977" )) obj.decode() self.assertListEqual(obj.value, [ '0x65d2273adeb04478658e183dc5edf41f1d86e42255442af62e72dbf1e6c0b977', '0x65d2273adeb04478658e183dc5edf41f1d86e42255442af62e72dbf1e6c0b977' ])
def process(self): value = super().process() try: call_obj = self.get_decoder_class(type_string='Call', data=ScaleBytes('0x{}'.format( self.raw_value)), metadata=self.metadata) return call_obj.process() except: return value
def process_encode(self, value): if self.type_mapping: if type(value) == str: # Convert simple enum values value = {value: None} if type(value) != dict: raise ValueError( "Value must be a dict when type_mapping is set, not '{}'". format(value)) if len(value) != 1: raise ValueError( "Value for enum with type_mapping can only have one value") for enum_key, enum_value in value.items(): for idx, (item_key, item_value) in enumerate(self.type_mapping): if item_key == enum_key: self.index = idx struct_obj = self.get_decoder_class( type_string='Struct', type_mapping=[self.type_mapping[self.index]], runtime_config=self.runtime_config) return ScaleBytes(bytearray( [self.index])) + struct_obj.encode(value) raise ValueError( "Value '{}' not present in type_mapping of this enum". format(enum_key)) else: for idx, item in enumerate(self.value_list): if item == value: self.index = idx return ScaleBytes(bytearray([self.index])) raise ValueError( "Value '{}' not present in value list of this enum".format( value))
def test_dynamic_set(self): RuntimeConfiguration().update_type_registry( load_type_registry_preset("default")) obj = ScaleDecoder.get_decoder_class('WithdrawReasons', ScaleBytes("0x0100000000000000")) obj.decode() self.assertEqual(obj.value, ["TransactionPayment"]) obj = ScaleDecoder.get_decoder_class('WithdrawReasons', ScaleBytes("0x0300000000000000")) obj.decode() self.assertEqual(obj.value, ["TransactionPayment", "Transfer"]) obj = ScaleDecoder.get_decoder_class('WithdrawReasons', ScaleBytes("0x1600000000000000")) obj.decode() self.assertEqual(obj.value, ["Transfer", "Reserve", "Tip"])
def test_era_mortal(self): obj = RuntimeConfiguration().create_scale_object( 'Era', ScaleBytes('0x4e9c')) obj.decode() self.assertTupleEqual(obj.value, (32768, 20000)) self.assertEqual(obj.period, 32768) self.assertEqual(obj.phase, 20000) obj = RuntimeConfiguration().create_scale_object( 'Era', ScaleBytes('0xc503')) obj.decode() self.assertTupleEqual(obj.value, (64, 60)) self.assertEqual(obj.period, 64) self.assertEqual(obj.phase, 60) obj = RuntimeConfiguration().create_scale_object( 'Era', ScaleBytes('0x8502')) obj.decode() self.assertTupleEqual(obj.value, (64, 40)) self.assertEqual(obj.period, 64) self.assertEqual(obj.phase, 40)
def test_compact_balance_encode_decode(self): scale_data = ScaleBytes('0x070010a5d4e8') value = 1000000000000 obj = ScaleDecoder.get_decoder_class('Compact<Balance>') data = obj.encode(value) self.assertEqual(str(scale_data), str(data)) obj_check = ScaleDecoder.get_decoder_class('Compact<Balance>', data) self.assertEqual(obj_check.decode(), value)
def encode(self, value: int): if value <= 0b00111111: self.data = ScaleBytes(bytearray(int(value << 2).to_bytes(1, 'little'))) elif value <= 0b0011111111111111: self.data = ScaleBytes(bytearray(int((value << 2) | 0b01).to_bytes(2, 'little'))) elif value <= 0b00111111111111111111111111111111: self.data = ScaleBytes(bytearray(int((value << 2) | 0b10).to_bytes(4, 'little'))) else: for bytes_length in range(5, 68): if 2 ** (8 * (bytes_length-1)) <= value < 2 ** (8 * bytes_length): self.data = ScaleBytes(bytearray(((bytes_length - 4) << 2 | 0b11).to_bytes(1, 'little') + value.to_bytes(bytes_length, 'little'))) break else: raise ValueError('{} out of range'.format(value)) return self.data
def test_wrapped_opaque_decode_fail(self): opaque_hex = '0x180a000022db73' wrapped_obj = self.runtime_config_v14.create_scale_object( type_string="WrapperKeepOpaque", metadata=self.metadata_v14_obj ) wrapped_obj.type_mapping = ("Compact<u32>", "Call") wrapped_obj.decode(ScaleBytes(opaque_hex)) self.assertEqual( "0x0a000022db73", wrapped_obj.value )
def test_box_call(self): RuntimeConfiguration().update_type_registry(load_type_registry_preset("default")) scale_value = ScaleBytes("0x0400006e57561de4b4e63f0af8bf336008252a9597e5cdcb7622c72de4ff39731c5402070010a5d4e8") obj = ScaleDecoder.get_decoder_class('Box<Call>', scale_value, metadata=self.metadata_decoder) value = obj.decode() self.assertEqual(value['call_function'], 'transfer') self.assertEqual(value['call_module'], 'Balances') self.assertEqual(value['call_args'][0]['value'], '0x6e57561de4b4e63f0af8bf336008252a9597e5cdcb7622c72de4ff39731c5402') self.assertEqual(value['call_args'][1]['value'], 1000000000000)
def test_unnamed_struct_multiple_elements(self): # pallet_democracy::vote::PriorLock obj = self.runtime_config.create_scale_object( 'scale_info::377', ScaleBytes("0x0c00000022000000000000000000000000000000")) obj.decode() self.assertEqual((12, 34), obj.value) data = obj.encode((12, 34)) self.assertEqual(data.to_hex(), '0x0c00000022000000000000000000000000000000')
def test_compact_balance_encode_decode(self): scale_data = ScaleBytes('0x070010a5d4e8') value = 1000000000000 obj = RuntimeConfiguration().create_scale_object('Compact<Balance>') data = obj.encode(value) self.assertEqual(str(scale_data), str(data)) obj_check = RuntimeConfiguration().create_scale_object('Compact<Balance>', data) self.assertEqual(obj_check.decode(), value)
def process(self): result = {} for key, data_type in self.type_mapping: result[key] = self.process_type(data_type, metadata=self.metadata).value # Replace HexBytes with actual proposal result['proposal'] = Proposal(ScaleBytes(result['proposal']), metadata=self.metadata).decode() return result
def test_metadata_registry_decode_v14(self): metadata_obj = self.runtime_config.create_scale_object( "MetadataVersioned", data=ScaleBytes(self.metadata_fixture_dict['V14']) ) metadata_obj.decode() self.assertEqual(metadata_obj.value_object[1].index, 14) self.assertIsNotNone(metadata_obj.portable_registry) self.assertGreater(len(metadata_obj[1][1]['pallets']), 0) self.assertGreater(len(metadata_obj.value[1]['V14']['pallets']), 0) self.assertGreater(len(metadata_obj.get_signed_extensions().items()), 0)
class LogDigest(Enum): value_list = [ 'Other', 'AuthoritiesChange', 'ChangesTrieRoot', 'Seal', 'Consensus', 'SealV0', 'PreRuntime' ] # value_list = ['System', 'Consensus', 'Sharding', 'Crfg'] def __init__(self, data, **kwargs): self.log_type = None self.index_value = None super().__init__(data, **kwargs) def process(self): self.index = int(self.get_next_bytes(1).hex()) self.index_value = self.value_list[self.index] if self.data.__str__()[0:10] == '0x00180200': self.data = ScaleBytes('0x' + self.data.__str__()[10:18]) self.log_type = self.process_type('ShardInfoLog') return { 'type': self.log_type.type_string, 'value': self.log_type.value } if self.data.__str__()[0:10] == '0x00280400': self.data = ScaleBytes('0x' + self.data.__str__()[10:26]) self.log_type = self.process_type('U64') return {'type': 'Finalitytracker', 'value': self.log_type.value} if self.data.__str__()[0:12] == '0x00ed030300': self.data = ScaleBytes('0x' + self.data.__str__()[28:]) self.log_type = self.process_type('Vec<(SessionKey, u64)>') return {'type': 'Crfg', 'value': self.log_type.value} self.log_type = self.process_type(self.value_list[self.index]) return { 'type': self.log_type.type_string, 'value': self.log_type.value }
def process(self): self.index = int(self.get_next_bytes(1).hex()) self.index_value = self.value_list[self.index] if self.data.__str__()[0:10] == '0x00180200': self.data = ScaleBytes('0x' + self.data.__str__()[10:18]) self.log_type = self.process_type('ShardInfoLog') return { 'type': self.log_type.type_string, 'value': self.log_type.value } if self.data.__str__()[0:10] == '0x00280400': self.data = ScaleBytes('0x' + self.data.__str__()[10:26]) self.log_type = self.process_type('U64') return {'type': 'Finalitytracker', 'value': self.log_type.value} if self.data.__str__()[0:12] == '0x00ed030300': self.data = ScaleBytes('0x' + self.data.__str__()[28:]) self.log_type = self.process_type('Vec<(SessionKey, u64)>') return {'type': 'Crfg', 'value': self.log_type.value} self.log_type = self.process_type(self.value_list[self.index]) return { 'type': self.log_type.type_string, 'value': self.log_type.value }
def test_struct_encode_decode(self): value = {'unstake_threshold': 3, 'validator_payment': 0} scale_data = ScaleBytes("0x0c00") obj = RuntimeConfiguration().create_scale_object('ValidatorPrefsTo145') data = obj.encode(value) self.assertEqual(str(scale_data), str(data)) obj_check = RuntimeConfiguration().create_scale_object('ValidatorPrefsTo145', data) self.assertEqual(obj_check.decode(), value)
def setUpClass(cls): module_path = os.path.dirname(__file__) cls.metadata_fixture_dict = load_type_registry_file( os.path.join(module_path, 'fixtures', 'metadata_hex.json')) cls.runtime_config = RuntimeConfigurationObject( implements_scale_info=True) cls.runtime_config.update_type_registry( load_type_registry_preset("metadata_types")) cls.metadata_obj = cls.runtime_config.create_scale_object( 'MetadataVersioned', data=ScaleBytes(cls.metadata_fixture_dict['V14'])) cls.metadata_obj.decode()
def test_struct_encode_decode(self): value = {'unstakeThreshold': 3, 'validatorPayment': 0} scale_data = ScaleBytes("0x0c00") obj = ScaleDecoder.get_decoder_class('ValidatorPrefsLegacy') data = obj.encode(value) self.assertEqual(str(scale_data), str(data)) obj_check = ScaleDecoder.get_decoder_class('ValidatorPrefsLegacy', data) self.assertEqual(obj_check.decode(), value)
def test_named_struct(self): # scale_info::111 = ['frame_support', 'weights', 'RuntimeDbWeight'] obj = self.runtime_config.create_scale_object( 'scale_info::111', ScaleBytes("0xe110000000000000d204000000000000")) obj.decode() self.assertEqual(obj.value, {'read': 4321, 'write': 1234}) obj.encode({'read': 4321, 'write': 1234}) self.assertEqual(obj.data.to_hex(), '0xe110000000000000d204000000000000')
def process_encode(self, value): value = int(value) if value <= 0b00111111: return ScaleBytes(bytearray(int(value << 2).to_bytes(1, 'little'))) elif value <= 0b0011111111111111: return ScaleBytes(bytearray(int((value << 2) | 0b01).to_bytes(2, 'little'))) elif value <= 0b00111111111111111111111111111111: return ScaleBytes(bytearray(int((value << 2) | 0b10).to_bytes(4, 'little'))) else: for bytes_length in range(4, 68): if 2 ** (8 * (bytes_length - 1)) <= value < 2 ** (8 * bytes_length): return ScaleBytes(bytearray( ((bytes_length - 4) << 2 | 0b11).to_bytes(1, 'little') + value.to_bytes(bytes_length, 'little'))) else: raise ValueError('{} out of range'.format(value))
def test_enum_with_specified_index_number(self): RuntimeConfiguration().update_type_registry( load_type_registry_preset("test")) obj = RuntimeConfiguration().create_scale_object('EnumSpecifiedIndex') data = obj.encode("KSM") self.assertEqual("0x82", data.to_hex()) obj = RuntimeConfiguration().create_scale_object( 'EnumSpecifiedIndex', data=ScaleBytes("0x80")) self.assertEqual("KAR", obj.decode())
def ss58_decode_account_index(address, valid_address_type=None): account_index_bytes = ss58_decode(address, valid_address_type) if len(account_index_bytes) == 2: return ScaleDecoder.get_decoder_class( 'u8', data=ScaleBytes('0x{}'.format(account_index_bytes))).decode() if len(account_index_bytes) == 4: return ScaleDecoder.get_decoder_class( 'u16', data=ScaleBytes('0x{}'.format(account_index_bytes))).decode() if len(account_index_bytes) == 8: return ScaleDecoder.get_decoder_class( 'u32', data=ScaleBytes('0x{}'.format(account_index_bytes))).decode() if len(account_index_bytes) == 16: return ScaleDecoder.get_decoder_class( 'u64', data=ScaleBytes('0x{}'.format(account_index_bytes))).decode() else: raise ValueError("Invalid account index length")
def test_bounded_vec(self): # 'scale_info::90' = frame_support::storage::bounded_vec::BoundedVec obj = self.runtime_config.create_scale_object('scale_info::90', ScaleBytes("0x084345")) obj.decode() self.assertEqual('CE', obj.value) data = obj.encode([67, 69]) self.assertEqual('0x084345', data.to_hex()) data = obj.encode('CE') self.assertEqual('0x084345', data.to_hex())
def generate_hash(self): if self.contains_transaction: if self.extrinsic_length: extrinsic_data = self.data.data else: # Fallback for legacy version, prefix additional Compact<u32> with length extrinsic_length_type = CompactU32(ScaleBytes(bytearray())) extrinsic_length_type.encode(self.data.length) extrinsic_data = extrinsic_length_type.data.data + self.data.data return blake2b(extrinsic_data, digest_size=32).digest().hex() else: return None