Skip to content

Commit 1f85164

Browse files
authored
Merge pull request #2572 from opentensor/feat/thewhaleking/use-library
Uses the new async_substrate_interface lib
2 parents b31764f + 344a412 commit 1f85164

File tree

14 files changed

+49
-4842
lines changed

14 files changed

+49
-4842
lines changed

bittensor/core/async_subtensor.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import argparse
22
import asyncio
33
import copy
4-
from itertools import chain
54
import ssl
5+
from itertools import chain
66
from typing import Optional, Any, Union, Iterable, TYPE_CHECKING
77

88
import aiohttp
99
import asyncstdlib as a
1010
import numpy as np
1111
import scalecodec
12+
from async_substrate_interface import AsyncSubstrateInterface
1213
from bittensor_wallet.utils import SS58_FORMAT
1314
from numpy.typing import NDArray
1415
from scalecodec import GenericCall, ScaleType
@@ -28,7 +29,6 @@
2829
custom_rpc_type_registry,
2930
decode_account_id,
3031
)
31-
3232
from bittensor.core.config import Config
3333
from bittensor.core.errors import SubstrateRequestException
3434
from bittensor.core.extrinsics.asyncex.commit_reveal import commit_reveal_v3_extrinsic
@@ -60,8 +60,8 @@
6060
reveal_weights_extrinsic,
6161
)
6262
from bittensor.core.metagraph import AsyncMetagraph
63-
from bittensor.core.types import ParamWithTypes
6463
from bittensor.core.settings import version_as_int, TYPE_REGISTRY, DELEGATES_DETAILS_URL
64+
from bittensor.core.types import ParamWithTypes
6565
from bittensor.utils import (
6666
decode_hex_identity_dict,
6767
format_error_message,
@@ -72,19 +72,17 @@
7272
execute_coroutine,
7373
)
7474
from bittensor.utils import networking
75-
from bittensor.utils.substrate_interface import AsyncSubstrateInterface
7675
from bittensor.utils.balance import Balance
7776
from bittensor.utils.btlogging import logging
7877
from bittensor.utils.delegates_details import DelegatesDetails
7978
from bittensor.utils.weight_utils import generate_weight_hash
8079

81-
8280
if TYPE_CHECKING:
8381
from scalecodec import ScaleType
8482
from bittensor_wallet import Wallet
8583
from bittensor.core.axon import Axon
8684
from bittensor.utils import Certificate
87-
from bittensor.utils.substrate_interface import QueryMapResult
85+
from async_substrate_interface import QueryMapResult
8886

8987

9088
def _decode_hex_identity_dict(info_dictionary: dict[str, Any]) -> dict[str, Any]:

bittensor/core/errors.py

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,20 @@
1-
# The MIT License (MIT)
2-
# Copyright © 2024 Opentensor Foundation
3-
#
4-
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5-
# documentation files (the “Software”), to deal in the Software without restriction, including without limitation
6-
# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
7-
# and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8-
#
9-
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of
10-
# the Software.
11-
#
12-
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
13-
# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
14-
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
15-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
16-
# DEALINGS IN THE SOFTWARE.
17-
18-
from __future__ import annotations
19-
201
from typing import Optional, TYPE_CHECKING
212

3+
from async_substrate_interface.errors import (
4+
SubstrateRequestException,
5+
StorageFunctionNotFound,
6+
BlockNotFound,
7+
ExtrinsicNotFound,
8+
)
9+
2210
if TYPE_CHECKING:
2311
from bittensor.core.synapse import Synapse
2412

25-
26-
class SubstrateRequestException(Exception):
27-
pass
28-
29-
30-
class StorageFunctionNotFound(ValueError):
31-
pass
32-
33-
34-
class BlockNotFound(Exception):
35-
pass
36-
37-
38-
class ExtrinsicNotFound(Exception):
39-
pass
13+
# redundant aliases
14+
SubstrateRequestException = SubstrateRequestException
15+
StorageFunctionNotFound = StorageFunctionNotFound
16+
BlockNotFound = BlockNotFound
17+
ExtrinsicNotFound = ExtrinsicNotFound
4018

4119

4220
class ChainError(SubstrateRequestException):

bittensor/core/extrinsics/utils.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22

33
from typing import TYPE_CHECKING
44

5-
from bittensor.utils.btlogging import logging
5+
from async_substrate_interface.errors import SubstrateRequestException
6+
67
from bittensor.utils import format_error_message
7-
from bittensor.utils.substrate_interface import SubstrateRequestException
8+
from bittensor.utils.btlogging import logging
89

910
if TYPE_CHECKING:
1011
from bittensor.core.subtensor import Subtensor
1112
from bittensor.core.async_subtensor import AsyncSubtensor
12-
from bittensor.utils.substrate_interface import (
13+
from async_substrate_interface import (
1314
AsyncExtrinsicReceipt,
1415
ExtrinsicReceipt,
1516
)

bittensor/core/subtensor.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
from typing import TYPE_CHECKING, Any, Iterable, Optional, Union
33

44
import numpy as np
5+
from async_substrate_interface import SubstrateInterface
56
from numpy.typing import NDArray
67

78
from bittensor.core.async_subtensor import AsyncSubtensor
89
from bittensor.core.metagraph import Metagraph
910
from bittensor.core.settings import version_as_int
10-
from bittensor.utils.substrate_interface import SubstrateInterface
1111
from bittensor.utils import execute_coroutine, torch, get_event_loop
1212

1313
if TYPE_CHECKING:
@@ -23,7 +23,7 @@
2323
from bittensor.core.chain_data.subnet_info import SubnetInfo
2424
from bittensor.utils.balance import Balance
2525
from bittensor.utils import Certificate
26-
from bittensor.utils.substrate_interface import QueryMapResult
26+
from async_substrate_interface import QueryMapResult
2727
from bittensor.utils.delegates_details import DelegatesDetails
2828
from scalecodec.types import ScaleType
2929

bittensor/utils/__init__.py

Lines changed: 15 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,22 @@
1-
# The MIT License (MIT)
2-
# Copyright © 2024 Opentensor Foundation
3-
#
4-
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5-
# documentation files (the “Software”), to deal in the Software without restriction, including without limitation
6-
# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
7-
# and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8-
#
9-
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of
10-
# the Software.
11-
#
12-
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
13-
# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
14-
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
15-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
16-
# DEALINGS IN THE SOFTWARE.
17-
18-
import asyncio
191
import ast
20-
import base58
21-
from collections import namedtuple
222
import hashlib
23-
from hashlib import blake2b
24-
from typing import Any, Literal, Union, Optional, TYPE_CHECKING, Coroutine
3+
from collections import namedtuple
4+
from typing import Any, Literal, Union, Optional, TYPE_CHECKING
255
from urllib.parse import urlparse
266

277
import scalecodec
8+
from async_substrate_interface.utils import (
9+
event_loop_is_running,
10+
hex_to_bytes,
11+
get_event_loop,
12+
execute_coroutine,
13+
)
2814
from bittensor_wallet import Keypair
15+
from bittensor_wallet.errors import KeyFileError, PasswordError
16+
from scalecodec import ss58_decode, is_valid_ss58_address as _is_valid_ss58_address
2917

3018
from bittensor.core.settings import SS58_FORMAT
3119
from bittensor.utils.btlogging import logging
32-
from bittensor_wallet.errors import KeyFileError, PasswordError
3320
from .registration import torch, use_torch
3421
from .version import version_checking, check_version, VersionCheckError
3522

@@ -44,6 +31,11 @@
4431
version_checking = version_checking
4532
check_version = check_version
4633
VersionCheckError = VersionCheckError
34+
ss58_decode = ss58_decode
35+
event_loop_is_running = event_loop_is_running
36+
hex_to_bytes = hex_to_bytes
37+
get_event_loop = get_event_loop
38+
execute_coroutine = execute_coroutine
4739

4840

4941
RAOPERTAO = 1e9
@@ -54,118 +46,6 @@
5446
UnlockStatus = namedtuple("UnlockStatus", ["success", "message"])
5547

5648

57-
def ss58_decode(address: str, valid_ss58_format: Optional[int] = None) -> str:
58-
"""
59-
Decodes given SS58 encoded address to an account ID
60-
61-
Args:
62-
address: e.g. EaG2CRhJWPb7qmdcJvy3LiWdh26Jreu9Dx6R1rXxPmYXoDk
63-
valid_ss58_format: the format for what is considered valid
64-
65-
Returns:
66-
Decoded string AccountId
67-
"""
68-
69-
# Check if address is already decoded
70-
if address.startswith("0x"):
71-
return address
72-
73-
if address == "":
74-
raise ValueError("Empty address provided")
75-
76-
checksum_prefix = b"SS58PRE"
77-
78-
address_decoded = base58.b58decode(address)
79-
80-
if address_decoded[0] & 0b0100_0000:
81-
ss58_format_length = 2
82-
ss58_format = (
83-
((address_decoded[0] & 0b0011_1111) << 2)
84-
| (address_decoded[1] >> 6)
85-
| ((address_decoded[1] & 0b0011_1111) << 8)
86-
)
87-
else:
88-
ss58_format_length = 1
89-
ss58_format = address_decoded[0]
90-
91-
if ss58_format in [46, 47]:
92-
raise ValueError(f"{ss58_format} is a reserved SS58 format")
93-
94-
if valid_ss58_format is not None and ss58_format != valid_ss58_format:
95-
raise ValueError("Invalid SS58 format")
96-
97-
# Determine checksum length according to length of address string
98-
if len(address_decoded) in [3, 4, 6, 10]:
99-
checksum_length = 1
100-
elif len(address_decoded) in [
101-
5,
102-
7,
103-
11,
104-
34 + ss58_format_length,
105-
35 + ss58_format_length,
106-
]:
107-
checksum_length = 2
108-
elif len(address_decoded) in [8, 12]:
109-
checksum_length = 3
110-
elif len(address_decoded) in [9, 13]:
111-
checksum_length = 4
112-
elif len(address_decoded) in [14]:
113-
checksum_length = 5
114-
elif len(address_decoded) in [15]:
115-
checksum_length = 6
116-
elif len(address_decoded) in [16]:
117-
checksum_length = 7
118-
elif len(address_decoded) in [17]:
119-
checksum_length = 8
120-
else:
121-
raise ValueError("Invalid address length")
122-
123-
checksum = blake2b(checksum_prefix + address_decoded[0:-checksum_length]).digest()
124-
125-
if checksum[0:checksum_length] != address_decoded[-checksum_length:]:
126-
raise ValueError("Invalid checksum")
127-
128-
return address_decoded[
129-
ss58_format_length : len(address_decoded) - checksum_length
130-
].hex()
131-
132-
133-
def _is_valid_ss58_address(value: str, valid_ss58_format: Optional[int] = None) -> bool:
134-
"""
135-
Checks if given value is a valid SS58 formatted address, optionally check if address is valid for specified
136-
ss58_format
137-
138-
Args:
139-
value: value to checked
140-
valid_ss58_format: if valid_ss58_format is provided the address must be valid for specified ss58_format
141-
(network) as well
142-
143-
Returns:
144-
bool result
145-
"""
146-
147-
# Return False in case a public key is provided
148-
if value.startswith("0x"):
149-
return False
150-
151-
try:
152-
ss58_decode(value, valid_ss58_format=valid_ss58_format)
153-
except ValueError:
154-
return False
155-
156-
return True
157-
158-
159-
def event_loop_is_running() -> Optional[asyncio.AbstractEventLoop]:
160-
"""
161-
Simple function to check if event loop is running. Returns the loop if it is, otherwise None.
162-
"""
163-
try:
164-
return asyncio.get_running_loop()
165-
except RuntimeError:
166-
return None
167-
168-
16949
def ss58_to_vec_u8(ss58_address: str) -> list[int]:
17050
ss58_bytes: bytes = ss58_address_to_bytes(ss58_address)
17151
encoded_address: list[int] = [int(byte) for byte in ss58_bytes]
@@ -508,48 +388,3 @@ def unlock_key(wallet: "Wallet", unlock_type="coldkey") -> "UnlockStatus":
508388
except KeyFileError:
509389
err_msg = f"{unlock_type.capitalize()} keyfile is corrupt, non-writable, or non-readable, or non-existent."
510390
return UnlockStatus(False, err_msg)
511-
512-
513-
def hex_to_bytes(hex_str: str) -> bytes:
514-
"""
515-
Converts a hex-encoded string into bytes. Handles 0x-prefixed and non-prefixed hex-encoded strings.
516-
"""
517-
if hex_str.startswith("0x"):
518-
bytes_result = bytes.fromhex(hex_str[2:])
519-
else:
520-
bytes_result = bytes.fromhex(hex_str)
521-
return bytes_result
522-
523-
524-
def get_event_loop() -> asyncio.AbstractEventLoop:
525-
"""
526-
If an event loop is already running, returns that. Otherwise, creates a new event loop,
527-
and sets it as the main event loop for this thread, returning the newly-created event loop.
528-
"""
529-
if loop := event_loop_is_running():
530-
event_loop = loop
531-
else:
532-
event_loop = asyncio.get_event_loop()
533-
asyncio.set_event_loop(event_loop)
534-
return event_loop
535-
536-
537-
def execute_coroutine(
538-
coroutine: "Coroutine", event_loop: asyncio.AbstractEventLoop = None
539-
):
540-
"""
541-
Helper function to run an asyncio coroutine synchronously.
542-
543-
Args:
544-
coroutine (Coroutine): The coroutine to run.
545-
event_loop (AbstractEventLoop): The event loop to use. If `None`, attempts to fetch the already-running
546-
loop. If one is not running, a new loop is created.
547-
548-
Returns:
549-
The result of the coroutine execution.
550-
"""
551-
if event_loop:
552-
event_loop = event_loop
553-
else:
554-
event_loop = get_event_loop()
555-
return event_loop.run_until_complete(asyncio.wait_for(coroutine, timeout=None))

0 commit comments

Comments
 (0)