Skip to content

Commit fad97f6

Browse files
Merge pull request #2686 from opentensor/release/9.0.1
Release/9.0.1
2 parents 13b6b3c + c228c6c commit fad97f6

23 files changed

+315
-117
lines changed

CHANGELOG.md

+20
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
11
# Changelog
22

3+
## What's Changed
4+
* Release/9.0.0 by @ibraheem-opentensor in https://github.com/opentensor/bittensor/pull/2671
5+
* fix e2e test by @roman-opentensor in https://github.com/opentensor/bittensor/pull/2673
6+
* fix e2e test incentive by @roman-opentensor in https://github.com/opentensor/bittensor/pull/2674
7+
* Add compatibility for read-only systems by @Arthurdw in https://github.com/opentensor/bittensor/pull/2676
8+
* test: use asynccontextmanager for FastAPI lifespan by @zyzniewski-reef in https://github.com/opentensor/bittensor/pull/2597
9+
* test(2472): offline unittests by @zyzniewski-reef in https://github.com/opentensor/bittensor/pull/2596
10+
* Removes redundant assignments in Metagraph by @ibraheem-opentensor in https://github.com/opentensor/bittensor/pull/2680
11+
* Alpha str formatting by @thewhaleking in https://github.com/opentensor/bittensor/pull/2672
12+
* Add method for fetching all Neuron Certificates on a Netuid by @thewhaleking in https://github.com/opentensor/bittensor/pull/2677
13+
* Updates tao_stake in MetagraphInfo by @ibraheem-opentensor in https://github.com/opentensor/bittensor/pull/2682
14+
* fix(2188): configure uvicorn event loop by @zyzniewski-reef in https://github.com/opentensor/bittensor/pull/2679
15+
* Refactor AsyncSubtensor aenter logic by @thewhaleking in https://github.com/opentensor/bittensor/pull/2684
16+
* Backmerge master to staging 900 by @ibraheem-opentensor in https://github.com/opentensor/bittensor/pull/2685
17+
18+
## New Contributors
19+
* @Arthurdw made their first contribution in https://github.com/opentensor/bittensor/pull/2676
20+
21+
**Full Changelog**: https://github.com/opentensor/bittensor/compare/v9.0.0...v9.0.1
22+
323
## 9.0.0 /2025-02-13
424

525
## What's Changed

VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
9.0.0
1+
9.0.1

bittensor/core/async_subtensor.py

+37-2
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,8 @@ async def __aenter__(self):
173173
f"[magenta]Connecting to Substrate:[/magenta] [blue]{self}[/blue][magenta]...[/magenta]"
174174
)
175175
try:
176-
async with self.substrate:
177-
return self
176+
await self.substrate.initialize()
177+
return self
178178
except TimeoutError:
179179
logging.error(
180180
f"[red]Error[/red]: Timeout occurred connecting to substrate."
@@ -1479,6 +1479,41 @@ async def get_neuron_certificate(
14791479
return None
14801480
return None
14811481

1482+
async def get_all_neuron_certificates(
1483+
self,
1484+
netuid: int,
1485+
block: Optional[int] = None,
1486+
block_hash: Optional[str] = None,
1487+
reuse_block: bool = False,
1488+
) -> dict[str, Certificate]:
1489+
"""
1490+
Retrieves the TLS certificates for neurons within a specified subnet (netuid) of the Bittensor network.
1491+
1492+
Arguments:
1493+
netuid: The unique identifier of the subnet.
1494+
block: The blockchain block number for the query.
1495+
block_hash: The hash of the block to retrieve the parameter from. Do not specify if using block or
1496+
reuse_block.
1497+
reuse_block: Whether to use the last-used block. Do not set if using block_hash or block.
1498+
1499+
Returns:
1500+
{ss58: Certificate} for the key/Certificate pairs on the subnet
1501+
1502+
This function is used for certificate discovery for setting up mutual tls communication between neurons.
1503+
"""
1504+
query_certificates = await self.query_map(
1505+
module="SubtensorModule",
1506+
name="NeuronCertificates",
1507+
params=[netuid],
1508+
block=block,
1509+
block_hash=block_hash,
1510+
reuse_block=reuse_block,
1511+
)
1512+
output = {}
1513+
async for key, item in query_certificates:
1514+
output[decode_account_id(key)] = Certificate(item.value)
1515+
return output
1516+
14821517
async def get_neuron_for_pubkey_and_subnet(
14831518
self,
14841519
hotkey_ss58: str,

bittensor/core/axon.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,11 @@ def __init__(
386386
self.app = FastAPI()
387387
log_level = "trace" if logging.__trace_on__ else "critical"
388388
self.fast_config = uvicorn.Config(
389-
self.app, host="0.0.0.0", port=self._config.axon.port, log_level=log_level
389+
self.app,
390+
host="0.0.0.0",
391+
log_level=log_level,
392+
loop="none",
393+
port=self._config.axon.port,
390394
)
391395
self.fast_server = FastAPIThreadedServer(config=self.fast_config)
392396
self.router = APIRouter()

bittensor/core/chain_data/metagraph_info.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from dataclasses import dataclass
22
from typing import Optional, Union
33

4+
from bittensor.core import settings
45
from bittensor.core.chain_data.axon_info import AxonInfo
56
from bittensor.core.chain_data.chain_identity import ChainIdentity
67
from bittensor.core.chain_data.info_base import InfoBase
@@ -234,7 +235,10 @@ def _from_dict(cls, decoded: dict) -> "MetagraphInfo":
234235
rank=[u16tf(rk) for rk in decoded.get("rank", [])],
235236
block_at_registration=decoded["block_at_registration"],
236237
alpha_stake=[_tbwu(ast, _netuid) for ast in decoded["alpha_stake"]],
237-
tao_stake=[_tbwu(ts) for ts in decoded["tao_stake"]],
238+
tao_stake=[
239+
_tbwu(ts) * settings.ROOT_TAO_STAKE_WEIGHT
240+
for ts in decoded["tao_stake"]
241+
],
238242
total_stake=[_tbwu(ts, _netuid) for ts in decoded["total_stake"]],
239243
# Dividend break down
240244
tao_dividends_per_hotkey=[

bittensor/core/metagraph.py

+9-12
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242

4343

4444
Tensor = Union["torch.nn.Parameter", NDArray]
45-
ROOT_TAO_STAKES_WEIGHT = 0.18
4645

4746

4847
METAGRAPH_STATE_DICT_NDARRAY_KEYS = [
@@ -773,14 +772,6 @@ def _set_metagraph_attributes(self, block: int):
773772
[neuron.validator_trust for neuron in self.neurons],
774773
dtype=self._dtype_registry["float32"],
775774
)
776-
self.total_stake = self._create_tensor(
777-
[neuron.total_stake.tao for neuron in self.neurons],
778-
dtype=self._dtype_registry["float32"],
779-
)
780-
self.stake = self._create_tensor(
781-
[neuron.stake.tao for neuron in self.neurons],
782-
dtype=self._dtype_registry["float32"],
783-
)
784775
self.axons = [n.axon_info for n in self.neurons]
785776

786777
def save(self, root_dir: Optional[list[str]] = None) -> "MetagraphMixin":
@@ -1606,7 +1597,10 @@ async def _get_all_stakes_from_chain(self):
16061597
dtype=self._dtype_registry["float32"],
16071598
)
16081599
self.tao_stake = self._create_tensor(
1609-
[b.tao * ROOT_TAO_STAKES_WEIGHT for b in subnet_state.tao_stake],
1600+
[
1601+
b.tao * settings.ROOT_TAO_STAKE_WEIGHT
1602+
for b in subnet_state.tao_stake
1603+
],
16101604
dtype=self._dtype_registry["float32"],
16111605
)
16121606
self.total_stake = self.stake = self._create_tensor(
@@ -1634,7 +1628,7 @@ def __init__(
16341628
subtensor: Optional["Subtensor"] = None,
16351629
):
16361630
super().__init__(netuid, network, lite, sync, subtensor)
1637-
if sync:
1631+
if self.should_sync:
16381632
self.sync()
16391633

16401634
def sync(
@@ -1910,7 +1904,10 @@ def _get_all_stakes_from_chain(self):
19101904
dtype=self._dtype_registry["float32"],
19111905
)
19121906
self.tao_stake = self._create_tensor(
1913-
[b.tao * ROOT_TAO_STAKES_WEIGHT for b in subnet_state.tao_stake],
1907+
[
1908+
b.tao * settings.ROOT_TAO_STAKE_WEIGHT
1909+
for b in subnet_state.tao_stake
1910+
],
19141911
dtype=self._dtype_registry["float32"],
19151912
)
19161913
self.total_stake = self.stake = self._create_tensor(

bittensor/core/settings.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
1-
__version__ = "9.0.0"
1+
__version__ = "9.0.1"
22

33
import os
44
import re
55
from pathlib import Path
66

77
from munch import munchify
88

9+
ROOT_TAO_STAKE_WEIGHT = 0.18
10+
11+
READ_ONLY = os.getenv("READ_ONLY") == "1"
912

1013
HOME_DIR = Path.home()
1114
USER_BITTENSOR_DIR = HOME_DIR / ".bittensor"
1215
WALLETS_DIR = USER_BITTENSOR_DIR / "wallets"
1316
MINERS_DIR = USER_BITTENSOR_DIR / "miners"
1417

1518

16-
# Create dirs if they don't exist
17-
WALLETS_DIR.mkdir(parents=True, exist_ok=True)
18-
MINERS_DIR.mkdir(parents=True, exist_ok=True)
19+
if not READ_ONLY:
20+
# Create dirs if they don't exist
21+
WALLETS_DIR.mkdir(parents=True, exist_ok=True)
22+
MINERS_DIR.mkdir(parents=True, exist_ok=True)
1923

2024
# Bittensor networks name
2125
NETWORKS = ["finney", "test", "archive", "local", "subvortex", "rao", "latent-lite"]

bittensor/core/subtensor.py

+26
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,32 @@ def get_neuron_certificate(
11231123
return None
11241124
return None
11251125

1126+
def get_all_neuron_certificates(
1127+
self, netuid: int, block: Optional[int] = None
1128+
) -> dict[str, Certificate]:
1129+
"""
1130+
Retrieves the TLS certificates for neurons within a specified subnet (netuid) of the Bittensor network.
1131+
1132+
Arguments:
1133+
netuid: The unique identifier of the subnet.
1134+
block: The blockchain block number for the query.
1135+
1136+
Returns:
1137+
{ss58: Certificate} for the key/Certificate pairs on the subnet
1138+
1139+
This function is used for certificate discovery for setting up mutual tls communication between neurons.
1140+
"""
1141+
query_certificates = self.query_map(
1142+
module="SubtensorModule",
1143+
name="NeuronCertificates",
1144+
params=[netuid],
1145+
block=block,
1146+
)
1147+
output = {}
1148+
for key, item in query_certificates:
1149+
output[decode_account_id(key)] = Certificate(item.value)
1150+
return output
1151+
11261152
def get_neuron_for_pubkey_and_subnet(
11271153
self, hotkey_ss58: str, netuid: int, block: Optional[int] = None
11281154
) -> Optional["NeuronInfo"]:

bittensor/utils/balance.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,13 @@ def __float__(self):
5353
return self.tao
5454

5555
def __str__(self):
56-
"""Returns the Balance object as a string in the format "symbolvalue", where the value is in tao."""
57-
return f"{self.unit}{float(self.tao):,.9f}"
56+
"""
57+
Returns the Balance object as a string in the format "symbolvalue", where the value is in tao.
58+
"""
59+
if self.unit == units[0]:
60+
return f"{self.unit}{float(self.tao):,.9f}"
61+
else:
62+
return f"\u200e{float(self.tao):,.9f}{self.unit}\u200e"
5863

5964
def __rich__(self):
6065
int_tao, fract_tao = format(float(self.tao), "f").split(".")

bittensor/utils/btlogging/loggingmachine.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from statemachine import State, StateMachine
1818

19+
from bittensor.core.settings import READ_ONLY
1920
from bittensor.core.config import Config
2021
from bittensor.utils.btlogging.console import BittensorConsole
2122
from .defines import (
@@ -584,9 +585,12 @@ def add_args(cls, parser: argparse.ArgumentParser, prefix: str = None):
584585
default_logging_info = os.getenv("BT_LOGGING_INFO") or False
585586
default_logging_trace = os.getenv("BT_LOGGING_TRACE") or False
586587
default_logging_record_log = os.getenv("BT_LOGGING_RECORD_LOG") or False
587-
default_logging_logging_dir = os.getenv(
588-
"BT_LOGGING_LOGGING_DIR"
589-
) or os.path.join("~", ".bittensor", "miners")
588+
default_logging_logging_dir = (
589+
None
590+
if READ_ONLY
591+
else os.getenv("BT_LOGGING_LOGGING_DIR")
592+
or os.path.join("~", ".bittensor", "miners")
593+
)
590594
parser.add_argument(
591595
"--" + prefix_str + "logging.debug",
592596
action="store_true",

tests/e2e_tests/test_commit_weights.py

+3
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ async def test_commit_weights_uses_next_nonce(local_chain, subtensor, alice_wall
167167
Raises:
168168
AssertionError: If any of the checks or verifications fail
169169
"""
170+
# Wait for 2 tempos to pass as CR3 only reveals weights after 2 tempos
171+
subtensor.wait_for_block(20)
172+
170173
netuid = 2
171174
print("Testing test_commit_and_reveal_weights")
172175
# Register root as Alice

tests/e2e_tests/test_incentive.py

+3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ async def test_incentive(local_chain, subtensor, templates, alice_wallet, bob_wa
2323
AssertionError: If any of the checks or verifications fail
2424
"""
2525

26+
# Wait for 2 tempos to spin up chain properly
27+
subtensor.wait_for_block(20)
28+
2629
print("Testing test_incentive")
2730
netuid = 2
2831

tests/e2e_tests/test_neuron_certificate.py

+3
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,8 @@ async def test_neuron_certificate(subtensor, alice_wallet):
4747
)
4848
== encoded_certificate
4949
)
50+
all_certs_query = subtensor.get_all_neuron_certificates(netuid=netuid)
51+
assert alice_wallet.hotkey.ss58_address in all_certs_query.keys()
52+
assert all_certs_query[alice_wallet.hotkey.ss58_address] == encoded_certificate
5053

5154
logging.info("✅ Passed test_neuron_certificate")

tests/unit_tests/conftest.py

+30
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import pytest
22
from aioresponses import aioresponses
3+
from async_substrate_interface.sync_substrate import SubstrateInterface
4+
5+
import bittensor.core.subtensor
36

47

58
@pytest.fixture
@@ -11,3 +14,30 @@ def force_legacy_torch_compatible_api(monkeypatch):
1114
def mock_aio_response():
1215
with aioresponses() as m:
1316
yield m
17+
18+
19+
@pytest.fixture
20+
def mock_substrate_interface(mocker):
21+
mocked = mocker.MagicMock(
22+
autospec=SubstrateInterface,
23+
)
24+
25+
mocker.patch("bittensor.core.subtensor.SubstrateInterface", return_value=mocked)
26+
27+
return mocked
28+
29+
30+
@pytest.fixture
31+
def subtensor(mock_substrate_interface):
32+
return bittensor.core.subtensor.Subtensor()
33+
34+
35+
@pytest.fixture
36+
def mock_get_external_ip(mocker):
37+
mocked = mocker.Mock(
38+
return_value="192.168.1.1",
39+
)
40+
41+
mocker.patch("bittensor.utils.networking.get_external_ip", mocked)
42+
43+
return mocked

tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
@pytest.fixture
1212
def subtensor(mocker):
1313
fake_substrate = mocker.AsyncMock()
14-
fake_substrate.websocket.sock.getsockopt.return_value = 0
14+
fake_substrate.websocket.socket.getsockopt.return_value = 0
1515
mocker.patch.object(
1616
subtensor_module, "AsyncSubstrateInterface", return_value=fake_substrate
1717
)

tests/unit_tests/extrinsics/test_commit_reveal.py

-11
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,6 @@
66
from bittensor.core import subtensor as subtensor_module
77
from bittensor.core.chain_data import SubnetHyperparameters
88
from bittensor.core.extrinsics import commit_reveal
9-
from bittensor.core.subtensor import Subtensor
10-
11-
12-
@pytest.fixture
13-
def subtensor(mocker):
14-
fake_substrate = mocker.MagicMock()
15-
fake_substrate.websocket.sock.getsockopt.return_value = 0
16-
mocker.patch.object(
17-
subtensor_module, "SubstrateInterface", return_value=fake_substrate
18-
)
19-
yield Subtensor()
209

2110

2211
@pytest.fixture

tests/unit_tests/extrinsics/test_commit_weights.py

+1-14
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,10 @@
1-
import pytest
21
from bittensor_wallet import Wallet
32

4-
from bittensor.core import subtensor as subtensor_module
5-
from bittensor.core.settings import version_as_int
6-
from bittensor.core.subtensor import Subtensor
73
from bittensor.core.extrinsics.commit_weights import (
84
_do_commit_weights,
95
_do_reveal_weights,
106
)
11-
12-
13-
@pytest.fixture
14-
def subtensor(mocker):
15-
fake_substrate = mocker.MagicMock()
16-
fake_substrate.websocket.sock.getsockopt.return_value = 0
17-
mocker.patch.object(
18-
subtensor_module, "SubstrateInterface", return_value=fake_substrate
19-
)
20-
return Subtensor()
7+
from bittensor.core.settings import version_as_int
218

229

2310
def test_do_commit_weights(subtensor, mocker):

0 commit comments

Comments
 (0)