def is_valid_substrate_address( chain: SubstrateChain, value: str, ) -> bool: """Validates a ss58 encoded substrate address for the given chain. Polkascan ss58 utils documentation: https://github.com/polkascan/py-substrate-interface/blob/master/substrateinterface/utils/ss58.py # noqa: E501 External Address Format (SS58) documentation: https://github.com/paritytech/substrate/wiki/External-Address-Format-(SS58) """ if chain == SubstrateChain.KUSAMA: valid_ss58_format = 2 else: raise AssertionError(f'Unexpected chain: {chain}') try: ss58_decode( address=value, valid_ss58_format=valid_ss58_format, ) except ValueError: return False return True
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_encode_with_2_byte_prefix(self): public_key = ss58_decode('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua') self.assertEqual( 'yGHU8YKprxHbHdEv7oUK4rzMZXtsdhcXVG2CAMyC9WhzhjH2k', ss58_encode(public_key, ss58_format=255) )
def is_valid_substrate_address( chain: SubstrateChain, value: str, ) -> bool: """Validates a ss58 encoded substrate address for the given chain. TODO: this function relies on py-scale-codec `ss58_decode()` for validating that a str is a valid substrate address for the given chain. The recent changes introduced on the py-scale-codec library have altered the behavior of this function by validating positively any str starting with `0x`. An issue has been opened regarding this matter (link below). Once py-scale-codec implements its own address validation method this function may be no longer needed. Issue: https://github.com/polkascan/py-scale-codec/issues/27 Polkascan ss58 utils documentation: https://github.com/polkascan/py-substrate-interface/blob/master/substrateinterface/utils/ss58.py # noqa: E501 External Address Format (SS58) documentation: https://github.com/paritytech/substrate/wiki/External-Address-Format-(SS58) """ if value.startswith('0x'): # TODO: temporary patch for py-scale-codec/issue/27 return False if chain == SubstrateChain.KUSAMA: valid_ss58_format = 2 elif chain == SubstrateChain.POLKADOT: valid_ss58_format = 0 else: raise AssertionError(f'Unexpected chain: {chain}') try: ss58_decode( address=value, valid_ss58_format=valid_ss58_format, ) except ValueError: return False return True
def get_escrow_address(addresses: list, *, threshold: int = 2): _addresses = [] for addr in addresses: _addr = ss58_decode(addr) _addr = bytes.fromhex(_addr) _addresses.append(_addr) _addresses = sorted(_addresses) print(_addresses) escrow_addr = multisig.multi_account_id(_addresses, threshold) escrow_addr = binascii.hexlify(bytearray(escrow_addr)).decode("ascii") return ss58_encode(escrow_addr, 2)
def setUp(self): self.buyer_address = ss58_encode(buyer_keypair[0], 2) self.seller_address = ss58_encode(seller_keypair[0], 2) self.admin_address = ss58_encode(admin_keypair[0], 2) self.seller_priv = seller_keypair[1].hex() self.seller_pub = ss58_decode(self.seller_address) self.admin_priv = admin_keypair[1].hex() self.admin_pub = ss58_decode(self.admin_address) substrate.init_runtime(block_hash=None) # Prepare the inner call as we will reuse it, lets not write code twice self.inner_call = ScaleDecoder.get_decoder_class( "Call", metadata=substrate.metadata_decoder) self.inner_call.encode({ "call_module": "Balances", "call_function": "transfer", "call_args": { "dest": self.buyer_address, "value": settings.trade_value, }, })
def check_transfer_config( config: 'bittensor.Config'): if config.subtensor.network == bittensor.defaults.subtensor.network and not config.no_prompt: config.subtensor.network = Prompt.ask("Enter subtensor network", choices=bittensor.__networks__, default = bittensor.defaults.subtensor.network) if config.wallet.name == bittensor.defaults.wallet.name and not config.no_prompt: wallet_name = Prompt.ask("Enter wallet name", default = bittensor.defaults.wallet.name) config.wallet.name = str(wallet_name) # Get destination. if not config.dest: dest = Prompt.ask("Enter destination public key: (ss58 or ed2519)") if len(dest) == 48: try: ss58_decode(dest) config.dest = str(dest) except ValueError: console.print(":cross_mark:[red] Invalid public key format[/red] [bold white]{}[/bold white]".format(dest)) sys.exit() elif len(dest) == 66 or len(dest) == 64: try: ss58_encode(dest) config.dest = str(dest) except ValueError: console.print(":cross_mark:[red] Invalid ss58 address format[/red] [bold white]{}[/bold white]".format(dest)) sys.exit() else: console.print(":cross_mark:[red] Invalid address format[/red] [bold white]{}[/bold white]".format(dest)) sys.exit() # Get amount. if not config.amount: amount = Prompt.ask("Enter Tao amount to transfer") try: config.amount = float(amount) except ValueError: console.print(":cross_mark:[red] Invalid Tao amount[/red] [bold white]{}[/bold white]".format(amount)) sys.exit()
def test_get_active(setup_chain): wallet = generate_wallet() subtensor = setup_subtensor(setup_chain) metagraph = bittensor.metagraph(subtensor=subtensor) subtensor.register(wallet=wallet) metagraph.sync() result = metagraph.active assert isinstance(result, Tensor) assert len(result) > 0 result = metagraph.hotkeys elem = result[0] elem = "0x" + ss58_decode(elem) assert isinstance(elem[0], str) assert elem[:2] == "0x" assert len(elem[2:]) == 64
def test_balance_check_rpc_breakdown(self): """ This test breaks down every RPC call that happens to get balance It is here only for the purpose of understanding how everything works Steps: Step 1: Initial preparatory steps - Connect to node - RPC Call 1: chain_getFinalisedHead - Get current finalised block hash Step 2: Get MetaData for the current block hash - RPC Call 2: state_getMetadata - Decode the result using MetadataDecoder and ScaleBytes Step 3: Prepare hashed data to make the next RPC call - We need three params hashed into one single hash string - Storage Module, Storage Function and Payload - Each param is encoded and hashed using various hashers Hasher for Payload is obtained from the dict at the bottom of the docstring Step 4: Get data at the storage hash prepared in Step 3 - RPC Call 3: state_getStorageAt - Decode the data using ScaleDecoder's special class This is a key information used to encode/hash/decode in the entire function { 'name': 'Account', 'modifier': 'Default', 'type': { 'MapType': { 'hasher': 'Blake2_128Concat', 'key': 'AccountId', 'value': 'AccountInfo<Index, AccountData>', 'isLinked': False } }, } """ # Test data test_address = "HsgNgA5sgjuKxGUeaZPJE8rRn9RuixjvnPkVLFUYLEpj15G" ### STEP 1 # Connect to the node substrate = SubstrateInterface( url=settings.NODE_URL, address_type=2, type_registry_preset="kusama", ) # Get finalised block hash block_hash = substrate.rpc_request("chain_getFinalisedHead", []).get("result") if not block_hash: raise Exception( "ERROR: RPC call for chain_getFinalisedHead failed") print("\n\n") print("-" * 100) print(f"BLOCK HASH: {block_hash}") ### STEP 2 # Get metadata decoder, this is needed later metadata_result = substrate.rpc_request("state_getMetadata", [block_hash]).get("result") if not metadata_result: raise Exception("ERROR: RPC call for state_getMetadata failed") metadata_encoded = MetadataDecoder(ScaleBytes(metadata_result)) metadata = metadata_encoded.decode() ### STEP 3 # This comes from the metadata dict in the docstring `type` -> `MapType` -> `key` map_type_key = "AccountId" test_address_modified = "0x{}".format(ss58.ss58_decode( test_address, 2)) print(f"TEST ADDRESS SS58 DECODED: {test_address_modified}") scale_decoder = ScaleDecoder.get_decoder_class(map_type_key) test_address_encoded = scale_decoder.encode(test_address_modified) print(f"TEST ADDRESS ENCODED: {test_address_encoded}") # Why blake2_128_concat? Because metadata dict in the docstring `type` -> `MapType` -> `hasher` test_address_hash = hasher.blake2_128_concat(test_address_encoded.data) # `System` is our module and `Account` if our function for this example storage_module = "System" storage_function = "Account" storage_module_hash = hasher.xxh128(storage_module.encode()) storage_function_hash = hasher.xxh128(storage_function.encode()) print(f"STORAGE MODULE: {storage_module}") print(f"STORAGE MODULE ENCODED: {storage_module.encode()}") print(f"STORAGE MODULE ENCODED HASHED: {storage_module_hash}") print(f"STORAGE FUNCTION: {storage_function}") print(f"STORAGE FUNCTION ENCODED: {storage_function.encode()}") print(f"STORAGE FUNCTION ENCODED HASHED: {storage_function_hash}") print(f"TEST ADDRESS: {test_address}") print(f"TEST ADDRESS SS58 DECODED: {test_address_modified}") print(f"TEST ADDRESS ENCODED: {test_address_encoded}") print(f"TEST ADDRESS ENCODED HASHED: {test_address_hash}") storage_hash = (f"0x" f"{storage_module_hash}" f"{storage_function_hash}" f"{test_address_hash}") print(f"COMBINED HASH: {storage_hash}") ### STEP 4 response = substrate.rpc_request("state_getStorageAt", [storage_hash, block_hash]) result = response.get("result") if not result: raise Exception("ERROR: RPC call for state_getStorageAt failed") print(f"RPC RESULT: {result}") print("-" * 100) print("DECODING ABOVE RESULT ... ...") # This is again extracted from the metadata dict in the docstring `type` -> `MapType` -> `value` return_type = "AccountInfo<Index, AccountData>" result_decoded = ScaleDecoder.get_decoder_class( return_type, ScaleBytes(result), metadata=metadata).decode() print(f"RESULT DECODED: {result_decoded}")
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_empty_string(self): with self.assertRaises(ValueError) as cm: ss58_decode('') self.assertEqual('Empty address provided', str(cm.exception))
def to_multi_address_account_id_filter(info, query, value): """ """ return Transfer.to_multi_address_account_id == ss58_decode(value)
def kusama_addr_to_account_id(address): decoded_addr = ss58_decode(address) decoded_addr_bytes = bytes.fromhex(decoded_addr) return decoded_addr_bytes
def multi_address_account_id_filter(info, query, value): """ """ return Extrinsic.multi_address_account_id == ss58_decode(value)
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_decode_public_key(self): self.assertEqual( '0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077', ss58_decode('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077') )
def test_invalid_ss58_format_check(self): with self.assertRaises(ValueError): ss58_decode('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua', valid_ss58_format=2)
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): ss58_decode('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQub')
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_invalid_length(self): with self.assertRaises(ValueError): ss58_decode( '5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQubsdhfjksdhfkj')