Skip to content

Commit 648f11a

Browse files
authoredFeb 27, 2025
Merge pull request #2712 from opentensor/release/9.0.3
2 parents b40a3c3 + 4f57bd1 commit 648f11a

22 files changed

+322
-128
lines changed
 

‎CHANGELOG.md

+24
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,29 @@
11
# Changelog
22

3+
## 9.0.3 /2025-02-26
4+
5+
## What's Changed
6+
* Release/9.0.2 by @ibraheem-opentensor in https://github.com/opentensor/bittensor/pull/2696
7+
* fix: typos in config test by @EricHasegawa in https://github.com/opentensor/bittensor/pull/2693
8+
* Removes limits in async + unstake_multiple by @ibraheem-opentensor in https://github.com/opentensor/bittensor/pull/2701
9+
* Fixes get_all_commitments, adds tests. by @thewhaleking in https://github.com/opentensor/bittensor/pull/2699
10+
* Use `.value` in e2e test by @thewhaleking in https://github.com/opentensor/bittensor/pull/2700
11+
* Fix e2e test setup by @thewhaleking in https://github.com/opentensor/bittensor/pull/2681
12+
* Dendrite `__del__` method fix by @thewhaleking in https://github.com/opentensor/bittensor/pull/2702
13+
* Fix E2E test_set_weights by @zyzniewski-reef in https://github.com/opentensor/bittensor/pull/2703
14+
* Updates test incentive by @ibraheem-opentensor in https://github.com/opentensor/bittensor/pull/2688
15+
* Add `get_timestamp` method by @thewhaleking in https://github.com/opentensor/bittensor/pull/2704
16+
* fix: async get_delegated by @zyzniewski-reef in https://github.com/opentensor/bittensor/pull/2706
17+
* Properly mock data_chain class methods by @zyzniewski-reef in https://github.com/opentensor/bittensor/pull/2705
18+
* Install btcli from install sh by @thewhaleking in https://github.com/opentensor/bittensor/pull/2708
19+
* Bumps dependencies of async substrate + btwallet by @ibraheem-opentensor in https://github.com/opentensor/bittensor/pull/2710
20+
* Backmerge/main to staging 902 by @ibraheem-opentensor in https://github.com/opentensor/bittensor/pull/2711
21+
22+
## New Contributors
23+
* @EricHasegawa made their first contribution in https://github.com/opentensor/bittensor/pull/2693
24+
25+
**Full Changelog**: https://github.com/opentensor/bittensor/compare/v9.0.2...v9.0.3
26+
327
## 9.0.2 /2025-02-24
428

529
## What's Changed

‎VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
9.0.2
1+
9.0.3

‎bittensor/core/async_subtensor.py

+34-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import asyncio
22
import copy
3+
from datetime import datetime, timezone
34
import ssl
45
from functools import partial
56
from typing import Optional, Any, Union, Iterable, TYPE_CHECKING
@@ -27,6 +28,7 @@
2728
decode_account_id,
2829
DynamicInfo,
2930
)
31+
from bittensor.core.chain_data.delegate_info import DelegatedInfo
3032
from bittensor.core.chain_data.utils import decode_metadata
3133
from bittensor.core.config import Config
3234
from bittensor.core.errors import SubstrateRequestException
@@ -999,7 +1001,7 @@ async def get_all_commitments(
9991001
)
10001002
result = {}
10011003
async for id_, value in query:
1002-
result[decode_account_id(id_[0])] = decode_account_id(value)
1004+
result[decode_account_id(id_[0])] = decode_metadata(value)
10031005
return result
10041006

10051007
async def get_current_weight_commit_info(
@@ -1219,7 +1221,7 @@ async def get_delegated(
12191221
if not result:
12201222
return []
12211223

1222-
return DelegateInfo.delegated_list_from_dicts(result)
1224+
return DelegatedInfo.list_from_dicts(result)
12231225

12241226
async def get_delegates(
12251227
self,
@@ -2744,6 +2746,36 @@ async def weights_rate_limit(
27442746
)
27452747
return None if call is None else int(call)
27462748

2749+
async def get_timestamp(
2750+
self,
2751+
block: Optional[int] = None,
2752+
block_hash: Optional[str] = None,
2753+
reuse_block: bool = False,
2754+
) -> datetime:
2755+
"""
2756+
Retrieves the datetime timestamp for a given block
2757+
2758+
Arguments:
2759+
block: The blockchain block number for the query. Do not specify if specifying block_hash or reuse_block.
2760+
block_hash: The blockchain block_hash representation of the block id. Do not specify if specifying block
2761+
or reuse_block.
2762+
reuse_block: Whether to reuse the last-used blockchain block hash. Do not specify if specifying block or
2763+
block_hash.
2764+
2765+
Returns:
2766+
datetime object for the timestamp of the block
2767+
"""
2768+
unix = (
2769+
await self.query_module(
2770+
"Timestamp",
2771+
"Now",
2772+
block=block,
2773+
block_hash=block_hash,
2774+
reuse_block=reuse_block,
2775+
)
2776+
).value
2777+
return datetime.fromtimestamp(unix / 1000, tz=timezone.utc)
2778+
27472779
# Extrinsics helper ================================================================================================
27482780

27492781
async def sign_and_send_extrinsic(

‎bittensor/core/dendrite.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -877,7 +877,14 @@ def __del__(self):
877877
# ... some operations ...
878878
del dendrite # This will implicitly invoke the __del__ method and close the session.
879879
"""
880-
self.close_session()
880+
try:
881+
self.close_session()
882+
except RuntimeError:
883+
if self._session:
884+
logging.debug(
885+
"A Dendrite session was unable to be closed during garbage-collection of the Dendrite object. This "
886+
"usually indicates that you were not using the async context manager."
887+
)
881888

882889

883890
# For back-compatibility with torch

‎bittensor/core/extrinsics/asyncex/staking.py

-14
Original file line numberDiff line numberDiff line change
@@ -314,20 +314,6 @@ async def add_stake_multiple_extrinsic(
314314
if staking_response is True: # If we successfully staked.
315315
# We only wait here if we expect finalization.
316316

317-
if idx < len(hotkey_ss58s) - 1:
318-
# Wait for tx rate limit.
319-
tx_query = await subtensor.substrate.query(
320-
module="SubtensorModule", storage_function="TxRateLimit"
321-
)
322-
tx_rate_limit_blocks: int = getattr(tx_query, "value", 0)
323-
if tx_rate_limit_blocks > 0:
324-
logging.error(
325-
f":hourglass: [yellow]Waiting for tx rate limit: [white]{tx_rate_limit_blocks}[/white] "
326-
f"blocks[/yellow]"
327-
)
328-
# 12 seconds per block
329-
await asyncio.sleep(tx_rate_limit_blocks * 12)
330-
331317
if not wait_for_finalization and not wait_for_inclusion:
332318
old_balance -= staking_balance
333319
successful_stakes += 1

‎bittensor/core/extrinsics/asyncex/unstaking.py

-12
Original file line numberDiff line numberDiff line change
@@ -281,18 +281,6 @@ async def unstake_multiple_extrinsic(
281281
if staking_response is True: # If we successfully unstaked.
282282
# We only wait here if we expect finalization.
283283

284-
if idx < len(hotkey_ss58s) - 1:
285-
# Wait for tx rate limit.
286-
tx_rate_limit_blocks = await subtensor.tx_rate_limit()
287-
if tx_rate_limit_blocks > 0:
288-
logging.info(
289-
f":hourglass: [yellow]Waiting for tx rate limit: "
290-
f"[white]{tx_rate_limit_blocks}[/white] blocks[/yellow]"
291-
)
292-
await asyncio.sleep(
293-
tx_rate_limit_blocks * 12
294-
) # 12 seconds per block
295-
296284
if not wait_for_finalization and not wait_for_inclusion:
297285
successful_unstakes += 1
298286
continue

‎bittensor/core/extrinsics/unstaking.py

-11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import time
21
from typing import Optional, TYPE_CHECKING
32

43
from bittensor.core.errors import StakeError, NotRegisteredError
@@ -266,16 +265,6 @@ def unstake_multiple_extrinsic(
266265
if staking_response is True: # If we successfully unstaked.
267266
# We only wait here if we expect finalization.
268267

269-
if idx < len(hotkey_ss58s) - 1:
270-
# Wait for tx rate limit.
271-
tx_rate_limit_blocks = subtensor.tx_rate_limit()
272-
if tx_rate_limit_blocks > 0:
273-
logging.info(
274-
f":hourglass: [yellow]Waiting for tx rate limit: "
275-
f"[white]{tx_rate_limit_blocks}[/white] blocks[/yellow]"
276-
)
277-
time.sleep(tx_rate_limit_blocks * 12) # 12 seconds per block
278-
279268
if not wait_for_finalization and not wait_for_inclusion:
280269
successful_unstakes += 1
281270
continue

‎bittensor/core/settings.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "9.0.2"
1+
__version__ = "9.0.3"
22

33
import os
44
import re

‎bittensor/core/subtensor.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import copy
2+
from datetime import datetime, timezone
3+
24
from functools import lru_cache
35
from typing import TYPE_CHECKING, Any, Iterable, Optional, Union, cast
46

@@ -754,7 +756,7 @@ def get_all_commitments(
754756
)
755757
result = {}
756758
for id_, value in query:
757-
result[decode_account_id(id_[0])] = decode_account_id(value)
759+
result[decode_account_id(id_[0])] = decode_metadata(value)
758760
return result
759761

760762
def get_current_weight_commit_info(
@@ -2070,6 +2072,19 @@ def weights_rate_limit(
20702072
)
20712073
return None if call is None else int(call)
20722074

2075+
def get_timestamp(self, block: Optional[int] = None) -> datetime:
2076+
"""
2077+
Retrieves the datetime timestamp for a given block
2078+
2079+
Arguments:
2080+
block: The blockchain block number for the query.
2081+
2082+
Returns:
2083+
datetime object for the timestamp of the block
2084+
"""
2085+
unix = cast(ScaleObj, self.query_module("Timestamp", "Now", block=block)).value
2086+
return datetime.fromtimestamp(unix / 1000, tz=timezone.utc)
2087+
20732088
# Extrinsics helper ================================================================================================
20742089

20752090
def sign_and_send_extrinsic(

‎scripts/install.sh

+3-1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ linux_install_bittensor() {
105105
git clone https://github.com/opentensor/bittensor.git ~/.bittensor/bittensor/ 2> /dev/null || (cd ~/.bittensor/bittensor/ ; git fetch origin master ; git checkout master ; git pull --ff-only ; git reset --hard ; git clean -xdf)
106106
ohai "Installing bittensor"
107107
$python -m pip install -e ~/.bittensor/bittensor/
108+
$python -m pip install -U bittensor-cli
108109
exit_on_error $?
109110
}
110111

@@ -163,10 +164,11 @@ mac_update_pip() {
163164
}
164165

165166
mac_install_bittensor() {
166-
ohai "Cloning bittensor@text_prompting into ~/.bittensor/bittensor"
167+
ohai "Cloning bittensor into ~/.bittensor/bittensor"
167168
git clone https://github.com/opentensor/bittensor.git ~/.bittensor/bittensor/ 2> /dev/null || (cd ~/.bittensor/bittensor/ ; git fetch origin master ; git checkout master ; git pull --ff-only ; git reset --hard; git clean -xdf)
168169
ohai "Installing bittensor"
169170
$python -m pip install -e ~/.bittensor/bittensor/
171+
$python -m pip install -U bittensor-cli
170172
exit_on_error $?
171173
deactivate
172174
}

‎tests/e2e_tests/conftest.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88

99
import pytest
1010
from async_substrate_interface import SubstrateInterface
11-
from bittensor.core.subtensor import Subtensor
1211

12+
from bittensor.core.async_subtensor import AsyncSubtensor
13+
from bittensor.core.subtensor import Subtensor
1314
from bittensor.utils.btlogging import logging
1415
from tests.e2e_tests.utils.e2e_test_utils import (
1516
Templates,
@@ -101,6 +102,11 @@ def subtensor(local_chain):
101102
return Subtensor(network="ws://localhost:9944")
102103

103104

105+
@pytest.fixture
106+
def async_subtensor(local_chain):
107+
return AsyncSubtensor(network="ws://localhost:9944")
108+
109+
104110
@pytest.fixture
105111
def alice_wallet():
106112
keypair, wallet = setup_wallet("//Alice")

‎tests/e2e_tests/test_commit_weights.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ async def test_commit_weights_uses_next_nonce(local_chain, subtensor, alice_wall
267267
assert success is True
268268

269269
# Wait a few blocks
270-
await asyncio.sleep(2) # Wait for the txs to be included in the chain
270+
await asyncio.sleep(10) # Wait for the txs to be included in the chain
271271

272272
# Query the WeightCommits storage map for all three salts
273273
weight_commits = subtensor.query_module(

‎tests/e2e_tests/test_commitment.py

+58
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,61 @@ def test_commitment(subtensor, alice_wallet):
5151
netuid=1,
5252
uid=uid,
5353
)
54+
55+
assert (
56+
subtensor.get_all_commitments(netuid=1)[alice_wallet.hotkey.ss58_address]
57+
== "Hello World!"
58+
)
59+
60+
61+
@pytest.mark.asyncio
62+
async def test_commitment_async(async_subtensor, alice_wallet):
63+
async with async_subtensor as sub:
64+
with pytest.raises(SubstrateRequestException, match="AccountNotAllowedCommit"):
65+
await sub.set_commitment(
66+
alice_wallet,
67+
netuid=1,
68+
data="Hello World!",
69+
)
70+
71+
assert await sub.burned_register(
72+
alice_wallet,
73+
netuid=1,
74+
)
75+
76+
uid = await sub.get_uid_for_hotkey_on_subnet(
77+
alice_wallet.hotkey.ss58_address,
78+
netuid=1,
79+
)
80+
81+
assert uid is not None
82+
83+
assert "" == await sub.get_commitment(
84+
netuid=1,
85+
uid=uid,
86+
)
87+
88+
assert await sub.set_commitment(
89+
alice_wallet,
90+
netuid=1,
91+
data="Hello World!",
92+
)
93+
94+
with pytest.raises(
95+
SubstrateRequestException,
96+
match="CommitmentSetRateLimitExceeded",
97+
):
98+
await sub.set_commitment(
99+
alice_wallet,
100+
netuid=1,
101+
data="Hello World!",
102+
)
103+
104+
assert "Hello World!" == await sub.get_commitment(
105+
netuid=1,
106+
uid=uid,
107+
)
108+
109+
assert (await sub.get_all_commitments(netuid=1))[
110+
alice_wallet.hotkey.ss58_address
111+
] == "Hello World!"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from datetime import datetime
2+
import pytest
3+
4+
5+
@pytest.mark.asyncio
6+
async def test_get_timestamp(subtensor, async_subtensor, local_chain):
7+
with subtensor:
8+
block_number = subtensor.get_current_block()
9+
assert isinstance(
10+
subtensor.get_timestamp(), datetime
11+
) # verify it works with no block number specified
12+
sync_result = subtensor.get_timestamp(
13+
block=block_number
14+
) # verify it works with block number specified
15+
async with async_subtensor:
16+
assert isinstance(await async_subtensor.get_timestamp(), datetime)
17+
async_result = await async_subtensor.get_timestamp(block=block_number)
18+
assert sync_result == async_result

0 commit comments

Comments
 (0)