Skip to content

Commit f45c8b6

Browse files
authored
Merge branch 'staging' into patch-4
2 parents b46e39a + ec1683e commit f45c8b6

20 files changed

+394
-173
lines changed

.github/workflows/e2e-subtensor-tests.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ jobs:
3939
id: get-tests
4040
run: |
4141
test_files=$(find tests/e2e_tests -name "test*.py" | jq -R -s -c 'split("\n") | map(select(. != ""))')
42+
# keep it here for future debug
43+
# test_files=$(find tests/e2e_tests -type f -name "test*.py" | grep -E 'test_(incentive|commit_weights|set_weights)\.py$' | jq -R -s -c 'split("\n") | map(select(. != ""))')
4244
echo "::set-output name=test-files::$test_files"
4345
shell: bash
4446

@@ -61,7 +63,7 @@ jobs:
6163
path: subtensor-localnet.tar
6264

6365
# Job to run tests in parallel
64-
run:
66+
run-e2e-test:
6567
name: ${{ matrix.test-file }} / Python ${{ matrix.python-version }}
6668
needs:
6769
- find-tests
@@ -70,7 +72,7 @@ jobs:
7072
timeout-minutes: 45
7173
strategy:
7274
fail-fast: false # Allow other matrix jobs to run even if this job fails
73-
max-parallel: 32 # Set the maximum number of parallel jobs (same as we have cores in SubtensorCI runner)
75+
max-parallel: 32 # Set the maximum number of parallel jobs (same as we have cores in ubuntu-latest runner)
7476
matrix:
7577
os:
7678
- ubuntu-latest

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
## Internet-scale Neural Networks <!-- omit in toc -->
1313

14-
[Discord](https://discord.gg/qasY3HA9F9)[Network](https://taostats.io/)[Research](https://bittensor.com/whitepaper)
14+
[Discord](https://discord.gg/qasY3HA9F9)[Network](https://taostats.io/)[Research](https://bittensor.com/whitepaper)[Documentation](https://docs.bittensor.com)
1515

1616
</div>
1717

bittensor/core/async_subtensor.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1687,6 +1687,38 @@ async def get_neuron_for_pubkey_and_subnet(
16871687
reuse_block=reuse_block,
16881688
)
16891689

1690+
async def get_next_epoch_start_block(
1691+
self,
1692+
netuid: int,
1693+
block: Optional[int] = None,
1694+
block_hash: Optional[str] = None,
1695+
reuse_block: bool = False,
1696+
) -> Optional[int]:
1697+
"""
1698+
Calculates the first block number of the next epoch for the given subnet.
1699+
1700+
If `block` is not provided, the current chain block will be used. Epochs are
1701+
determined based on the subnet's tempo (i.e., blocks per epoch). The result
1702+
is the block number at which the next epoch will begin.
1703+
1704+
Args:
1705+
netuid (int): The unique identifier of the subnet.
1706+
block (Optional[int], optional): The reference block to calculate from.
1707+
If None, uses the current chain block height.
1708+
block_hash (Optional[int]): The blockchain block number at which to perform the query.
1709+
reuse_block (bool): Whether to reuse the last-used blockchain block hash.
1710+
1711+
1712+
Returns:
1713+
int: The block number at which the next epoch will start.
1714+
"""
1715+
block_hash = await self.determine_block_hash(block, block_hash, reuse_block)
1716+
if not block_hash and reuse_block:
1717+
block_hash = self.substrate.last_block_hash
1718+
block = await self.substrate.get_block_number(block_hash=block_hash)
1719+
tempo = await self.tempo(netuid=netuid, block_hash=block_hash)
1720+
return (((block // tempo) + 1) * tempo) + 1 if tempo else None
1721+
16901722
async def get_owned_hotkeys(
16911723
self,
16921724
coldkey_ss58: str,
@@ -3813,6 +3845,7 @@ async def set_weights(
38133845
wait_for_finalization: bool = False,
38143846
max_retries: int = 5,
38153847
block_time: float = 12.0,
3848+
period: int = 5,
38163849
):
38173850
"""
38183851
Sets the inter-neuronal weights for the specified neuron. This process involves specifying the influence or
@@ -3833,6 +3866,7 @@ async def set_weights(
38333866
``False``.
38343867
max_retries (int): The number of maximum attempts to set weights. Default is ``5``.
38353868
block_time (float): The amount of seconds for block duration. Default is 12.0 seconds.
3869+
period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5.
38363870
38373871
Returns:
38383872
tuple[bool, str]: ``True`` if the setting of weights is successful, False otherwise. And `msg`, a string
@@ -3907,6 +3941,7 @@ async def _blocks_weight_limit() -> bool:
39073941
version_key=version_key,
39083942
wait_for_inclusion=wait_for_inclusion,
39093943
wait_for_finalization=wait_for_finalization,
3944+
period=period,
39103945
)
39113946
except Exception as e:
39123947
logging.error(f"Error setting weights: {e}")

bittensor/core/extrinsics/asyncex/weights.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ async def set_weights_extrinsic(
287287
version_key: int = 0,
288288
wait_for_inclusion: bool = False,
289289
wait_for_finalization: bool = False,
290+
period: int = 5,
290291
) -> tuple[bool, str]:
291292
"""Sets the given weights and values on chain for wallet hotkey account.
292293
@@ -302,6 +303,7 @@ async def set_weights_extrinsic(
302303
returns ``False`` if the extrinsic fails to enter the block within the timeout.
303304
wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning
304305
``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout.
306+
period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5.
305307
306308
Returns:
307309
success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for
@@ -331,6 +333,7 @@ async def set_weights_extrinsic(
331333
version_key=version_key,
332334
wait_for_finalization=wait_for_finalization,
333335
wait_for_inclusion=wait_for_inclusion,
336+
period=period,
334337
)
335338

336339
if not wait_for_finalization and not wait_for_inclusion:

bittensor/core/extrinsics/set_weights.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ def set_weights_extrinsic(
9191
version_key: int = 0,
9292
wait_for_inclusion: bool = False,
9393
wait_for_finalization: bool = False,
94+
period: int = 5,
9495
) -> tuple[bool, str]:
9596
"""Sets the given weights and values on chain for wallet hotkey account.
9697
@@ -106,6 +107,7 @@ def set_weights_extrinsic(
106107
returns ``False`` if the extrinsic fails to enter the block within the timeout.
107108
wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning
108109
``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout.
110+
period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5.
109111
110112
Returns:
111113
success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for
@@ -135,6 +137,7 @@ def set_weights_extrinsic(
135137
version_key=version_key,
136138
wait_for_finalization=wait_for_finalization,
137139
wait_for_inclusion=wait_for_inclusion,
140+
period=period,
138141
)
139142

140143
if not wait_for_finalization and not wait_for_inclusion:

bittensor/core/metagraph.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import asyncio
2+
import contextlib
23
import copy
34
import os
45
import pickle
@@ -11,6 +12,7 @@
1112
import numpy as np
1213
from async_substrate_interface.errors import SubstrateRequestException
1314
from numpy.typing import NDArray
15+
from packaging import version
1416

1517
from bittensor.core import settings
1618
from bittensor.core.chain_data import (
@@ -143,6 +145,27 @@ def latest_block_path(dir_path: str) -> str:
143145
return latest_file_full_path
144146

145147

148+
def safe_globals():
149+
"""
150+
Context manager to load torch files for version 2.6+
151+
"""
152+
if version.parse(torch.__version__).release < version.parse("2.6").release:
153+
return contextlib.nullcontext()
154+
155+
np_core = (
156+
np._core if version.parse(np.__version__) >= version.parse("2.0.0") else np.core
157+
)
158+
allow_list = [
159+
np_core.multiarray._reconstruct,
160+
np.ndarray,
161+
np.dtype,
162+
type(np.dtype(np.uint32)),
163+
np.dtypes.Float32DType,
164+
bytes,
165+
]
166+
return torch.serialization.safe_globals(allow_list)
167+
168+
146169
class MetagraphMixin(ABC):
147170
"""
148171
The metagraph class is a core component of the Bittensor network, representing the neural graph that forms the
@@ -1124,7 +1147,8 @@ def load_from_path(self, dir_path: str) -> "MetagraphMixin":
11241147
"""
11251148

11261149
graph_file = latest_block_path(dir_path)
1127-
state_dict = torch.load(graph_file)
1150+
with safe_globals():
1151+
state_dict = torch.load(graph_file)
11281152
self.n = torch.nn.Parameter(state_dict["n"], requires_grad=False)
11291153
self.block = torch.nn.Parameter(state_dict["block"], requires_grad=False)
11301154
self.uids = torch.nn.Parameter(state_dict["uids"], requires_grad=False)
@@ -1256,7 +1280,8 @@ def load_from_path(self, dir_path: str) -> "MetagraphMixin":
12561280
try:
12571281
import torch as real_torch
12581282

1259-
state_dict = real_torch.load(graph_filename)
1283+
with safe_globals():
1284+
state_dict = real_torch.load(graph_filename)
12601285
for key in METAGRAPH_STATE_DICT_NDARRAY_KEYS:
12611286
state_dict[key] = state_dict[key].detach().numpy()
12621287
del real_torch

bittensor/core/subtensor.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,6 +1300,28 @@ def get_neuron_for_pubkey_and_subnet(
13001300

13011301
return NeuronInfo.from_dict(result)
13021302

1303+
def get_next_epoch_start_block(
1304+
self, netuid: int, block: Optional[int] = None
1305+
) -> Optional[int]:
1306+
"""
1307+
Calculates the first block number of the next epoch for the given subnet.
1308+
1309+
If `block` is not provided, the current chain block will be used. Epochs are
1310+
determined based on the subnet's tempo (i.e., blocks per epoch). The result
1311+
is the block number at which the next epoch will begin.
1312+
1313+
Args:
1314+
netuid (int): The unique identifier of the subnet.
1315+
block (Optional[int], optional): The reference block to calculate from.
1316+
If None, uses the current chain block height.
1317+
1318+
Returns:
1319+
int: The block number at which the next epoch will start.
1320+
"""
1321+
block = block or self.block
1322+
tempo = self.tempo(netuid=netuid, block=block)
1323+
return (((block // tempo) + 1) * tempo) + 1 if tempo else None
1324+
13031325
def get_owned_hotkeys(
13041326
self,
13051327
coldkey_ss58: str,
@@ -3100,6 +3122,7 @@ def set_weights(
31003122
wait_for_finalization: bool = False,
31013123
max_retries: int = 5,
31023124
block_time: float = 12.0,
3125+
period: int = 5,
31033126
) -> tuple[bool, str]:
31043127
"""
31053128
Sets the inter-neuronal weights for the specified neuron. This process involves specifying the influence or
@@ -3120,6 +3143,7 @@ def set_weights(
31203143
``False``.
31213144
max_retries (int): The number of maximum attempts to set weights. Default is ``5``.
31223145
block_time (float): The amount of seconds for block duration. Default is 12.0 seconds.
3146+
period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5.
31233147
31243148
Returns:
31253149
tuple[bool, str]: ``True`` if the setting of weights is successful, False otherwise. And `msg`, a string
@@ -3183,6 +3207,7 @@ def _blocks_weight_limit() -> bool:
31833207
version_key=version_key,
31843208
wait_for_inclusion=wait_for_inclusion,
31853209
wait_for_finalization=wait_for_finalization,
3210+
period=period,
31863211
)
31873212
except Exception as e:
31883213
logging.error(f"Error setting weights: {e}")

bittensor/utils/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,16 @@ def format_error_message(error_message: Union[dict, Exception]) -> str:
250250
err_docs = error_message.get("docs", [err_description])
251251
err_description = err_docs[0] if err_docs else err_description
252252

253+
elif error_message.get("code") and error_message.get("message"):
254+
err_type = error_message.get("code", err_name)
255+
err_name = "Custom type"
256+
err_description = error_message.get("message", err_description)
257+
258+
else:
259+
logging.error(
260+
f"String representation of real error_message: {str(error_message)}"
261+
)
262+
253263
return f"Subtensor returned `{err_name}({err_type})` error. This means: `{err_description}`."
254264

255265

contrib/CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ Explain the problem and include additional details to help maintainers reproduce
252252
* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior.
253253
* **Explain which behavior you expected to see instead and why.**
254254
* **Include screenshots and animated GIFs** which show you following the described steps and clearly demonstrate the problem. You can use [this tool](https://www.cockos.com/licecap/) to record GIFs on macOS and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux.
255-
* **If you're reporting that Bittensor crashed**, include a crash report with a stack trace from the operating system. On macOS, the crash report will be available in `Console.app` under "Diagnostic and usage information" > "User diagnostic reports". Include the crash report in the issue in a [code block](https://help.github.com/articles/markdown-basics/#multiple-lines), a [file attachment](https://help.github.com/articles/file-attachments-on-issues-and-pull-requests/), or put it in a [gist](https://gist.github.com/) and provide link to that gist.
255+
* **If you're reporting that Bittensor crashed**, include a crash report with a stack trace from the operating system. On macOS, the crash report will be available in `Console.app` under "Diagnostic and usage information" > "User diagnostic reports". Include the crash report in the issue in a [code block](https://help.github.com/articles/markdown-basics/#multiple-lines), a [file attachment](https://docs.github.com/articles/file-attachments-on-issues-and-pull-requests/), or put it in a [gist](https://gist.github.com/) and provide link to that gist.
256256
* **If the problem is related to performance or memory**, include a CPU profile capture with your report, if you're using a GPU then include a GPU profile capture as well. Look into the [PyTorch Profiler](https://pytorch.org/tutorials/recipes/recipes/profiler_recipe.html) to look at memory usage of your model.
257257
* **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened and share more information using the guidelines below.
258258

pyproject.toml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[build-system]
2-
requires = ["setuptools~=70.0.0", "wheel"]
2+
requires = ["setuptools>=70.0.0", "wheel"]
33
build-backend = "setuptools.build_meta"
44

55
[project]
@@ -40,11 +40,11 @@ dependencies = [
4040

4141
[project.optional-dependencies]
4242
dev = [
43-
"pytest==7.2.0",
44-
"pytest-asyncio==0.23.7",
45-
"pytest-mock==3.12.0",
46-
"pytest-split==0.8.0",
47-
"pytest-xdist==3.0.2",
43+
"pytest==8.3.5",
44+
"pytest-asyncio==0.26.0",
45+
"pytest-mock==3.14.0",
46+
"pytest-split==0.10.0",
47+
"pytest-xdist==3.6.1",
4848
"pytest-rerunfailures==10.2",
4949
"coveralls==3.3.1",
5050
"pytest-cov==4.0.0",
@@ -59,10 +59,10 @@ dev = [
5959
"aioresponses==0.7.6",
6060
"factory-boy==3.3.0",
6161
"types-requests",
62-
"torch>=1.13.1,<2.6.0"
62+
"torch>=1.13.1,<3.0"
6363
]
6464
torch = [
65-
"torch>=1.13.1,<2.6.0"
65+
"torch>=1.13.1,<3.0"
6666
]
6767
cli = [
6868
"bittensor-cli>=9.0.2"

0 commit comments

Comments
 (0)