Skip to content

Commit 86209dd

Browse files
authored
Merge branch 'staging' into fix/zyzniewski/unittests_warnings
2 parents 4114aa7 + a70bee8 commit 86209dd

33 files changed

+837
-315
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

CHANGELOG.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,35 @@
11
# Changelog
22

3+
## 9.3.0 /2025-04-09
4+
5+
## What's Changed
6+
* More E2E tests by @zyzniewski-reef in https://github.com/opentensor/bittensor/pull/2754
7+
* Fix E2E: fix wait_epoch and next_tempo by @zyzniewski-reef in https://github.com/opentensor/bittensor/pull/2753
8+
* Add all supported python versions to e2e tests workflow by @basfroman in https://github.com/opentensor/bittensor/pull/2761
9+
* update docker image name by @basfroman in https://github.com/opentensor/bittensor/pull/2760
10+
* Add pypi package version checker for `python -m bittensor` by @basfroman in https://github.com/opentensor/bittensor/pull/2762
11+
* Feat: set_children and get_pending_children methods by @zyzniewski-reef in https://github.com/opentensor/bittensor/pull/2752
12+
* Add logic for keep docker image up to date by @basfroman in https://github.com/opentensor/bittensor/pull/2765
13+
* Fix: CI/CD Set up Python version for E2E tests by @zyzniewski-reef in https://github.com/opentensor/bittensor/pull/2767
14+
* Fix E2E Tests: wait for new nonce by @zyzniewski-reef in https://github.com/opentensor/bittensor/pull/2768
15+
* Fix e2e `conftest.py` for legacy runner by @basfroman in https://github.com/opentensor/bittensor/pull/2769
16+
* Fix E2E with devnet-ready by @zyzniewski-reef in https://github.com/opentensor/bittensor/pull/2776
17+
* Add compatibility check for 3.13 by @thewhaleking in https://github.com/opentensor/bittensor/pull/2779
18+
* Fix E2E test_dendrite by making sure Alice is Top validator in Subnet by @zyzniewski-reef in https://github.com/opentensor/bittensor/pull/2780
19+
* Add get_owned_hotkeys to subtensor and async one + tests by @basfroman in https://github.com/opentensor/bittensor/pull/2766
20+
* Add drand-commitments by @basfroman in https://github.com/opentensor/bittensor/pull/2781
21+
* Missing f-string format by @zyzniewski-reef in https://github.com/opentensor/bittensor/pull/2785
22+
* bump version by @basfroman in https://github.com/opentensor/bittensor/pull/2786
23+
* Improvement and fix for https://github.com/opentensor/bittensor/pull/2781 by @basfroman in https://github.com/opentensor/bittensor/pull/2787
24+
* Add `stop_existing_test_containers` logic before run e2e test/s by @basfroman in https://github.com/opentensor/bittensor/pull/2790
25+
* Bump async substrate interface by @thewhaleking in https://github.com/opentensor/bittensor/pull/2788
26+
* Improve CRv3 functionality by @basfroman in https://github.com/opentensor/bittensor/pull/2791
27+
* Improve logic in Balance magic methods by @basfroman in https://github.com/opentensor/bittensor/pull/2764
28+
* Requirements update by @thewhaleking in https://github.com/opentensor/bittensor/pull/2789
29+
* remove Levenshtein requirement by @thewhaleking in https://github.com/opentensor/bittensor/pull/2802
30+
31+
**Full Changelog**: https://github.com/opentensor/bittensor/compare/v9.2.0...v9.3.0
32+
333
## 9.2.0 /2025-03-18
434

535
## What's Changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
# **Bittensor SDK** <!-- omit in toc -->
44
[![Discord Chat](https://img.shields.io/discord/308323056592486420.svg)](https://discord.gg/bittensor)
5+
[![CodeQL](https://github.com/opentensor/bittensor/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/opentensor/bittensor/actions)
56
[![PyPI version](https://badge.fury.io/py/bittensor.svg)](https://badge.fury.io/py/bittensor)
67
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
78

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
9.2.0
1+
9.3.0

bittensor/core/async_subtensor.py

Lines changed: 99 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import asyncio
22
import copy
3-
from datetime import datetime, timezone
43
import ssl
4+
from datetime import datetime, timezone
55
from functools import partial
66
from typing import Optional, Any, Union, Iterable, TYPE_CHECKING
77

88
import asyncstdlib as a
99
import numpy as np
1010
import scalecodec
1111
from async_substrate_interface import AsyncSubstrateInterface
12+
from bittensor_commit_reveal import get_encrypted_commitment
1213
from bittensor_wallet.utils import SS58_FORMAT
1314
from numpy.typing import NDArray
1415
from scalecodec import GenericCall
@@ -27,7 +28,6 @@
2728
decode_account_id,
2829
DynamicInfo,
2930
)
30-
from bittensor_commit_reveal import get_encrypted_commitment
3131
from bittensor.core.chain_data.chain_identity import ChainIdentity
3232
from bittensor.core.chain_data.delegate_info import DelegatedInfo
3333
from bittensor.core.chain_data.utils import (
@@ -38,17 +38,17 @@
3838
from bittensor.core.config import Config
3939
from bittensor.core.errors import ChainError, SubstrateRequestException
4040
from bittensor.core.extrinsics.asyncex.commit_reveal import commit_reveal_v3_extrinsic
41+
from bittensor.core.extrinsics.asyncex.move_stake import (
42+
transfer_stake_extrinsic,
43+
swap_stake_extrinsic,
44+
move_stake_extrinsic,
45+
)
4146
from bittensor.core.extrinsics.asyncex.registration import (
4247
burned_register_extrinsic,
4348
register_extrinsic,
4449
register_subnet_extrinsic,
4550
set_subnet_identity_extrinsic,
4651
)
47-
from bittensor.core.extrinsics.asyncex.move_stake import (
48-
transfer_stake_extrinsic,
49-
swap_stake_extrinsic,
50-
move_stake_extrinsic,
51-
)
5252
from bittensor.core.extrinsics.asyncex.root import (
5353
set_root_weights_extrinsic,
5454
root_register_extrinsic,
@@ -84,6 +84,7 @@
8484
decode_hex_identity_dict,
8585
float_to_u64,
8686
format_error_message,
87+
is_valid_ss58_address,
8788
torch,
8889
u16_normalized_float,
8990
u64_normalized_float,
@@ -1064,14 +1065,14 @@ async def get_all_commitments(
10641065
result[decode_account_id(id_[0])] = decode_metadata(value)
10651066
return result
10661067

1067-
async def get_revealed_commitment(
1068+
async def get_revealed_commitment_by_hotkey(
10681069
self,
10691070
netuid: int,
10701071
hotkey_ss58_address: Optional[str] = None,
10711072
block: Optional[int] = None,
10721073
block_hash: Optional[str] = None,
10731074
reuse_block: bool = False,
1074-
) -> Optional[tuple[int, str]]:
1075+
) -> Optional[tuple[tuple[int, str], ...]]:
10751076
"""Returns hotkey related revealed commitment for a given netuid.
10761077
10771078
Arguments:
@@ -1084,6 +1085,9 @@ async def get_revealed_commitment(
10841085
Returns:
10851086
result (tuple[int, str): A tuple of reveal block and commitment message.
10861087
"""
1088+
if not is_valid_ss58_address(address=hotkey_ss58_address):
1089+
raise ValueError(f"Invalid ss58 address {hotkey_ss58_address} provided.")
1090+
10871091
query = await self.query_module(
10881092
module="Commitments",
10891093
name="RevealedCommitments",
@@ -1092,15 +1096,50 @@ async def get_revealed_commitment(
10921096
block_hash=block_hash,
10931097
reuse_block=reuse_block,
10941098
)
1095-
return decode_revealed_commitment(query)
1099+
if query is None:
1100+
return None
1101+
return tuple(decode_revealed_commitment(pair) for pair in query)
1102+
1103+
async def get_revealed_commitment(
1104+
self,
1105+
netuid: int,
1106+
uid: int,
1107+
block: Optional[int] = None,
1108+
) -> Optional[tuple[tuple[int, str], ...]]:
1109+
"""Returns uid related revealed commitment for a given netuid.
1110+
1111+
Arguments:
1112+
netuid (int): The unique identifier of the subnetwork.
1113+
uid (int): The neuron uid to retrieve the commitment from.
1114+
block (Optional[int]): The block number to retrieve the commitment from. Default is ``None``.
1115+
1116+
Returns:
1117+
result (Optional[tuple[int, str]]: A tuple of reveal block and commitment message.
1118+
1119+
Example of result:
1120+
( (12, "Alice message 1"), (152, "Alice message 2") )
1121+
( (12, "Bob message 1"), (147, "Bob message 2") )
1122+
"""
1123+
try:
1124+
meta_info = await self.get_metagraph_info(netuid, block=block)
1125+
if meta_info:
1126+
hotkey_ss58_address = meta_info.hotkeys[uid]
1127+
else:
1128+
raise ValueError(f"Subnet with netuid {netuid} does not exist.")
1129+
except IndexError:
1130+
raise ValueError(f"Subnet {netuid} does not have a neuron with uid {uid}.")
1131+
1132+
return await self.get_revealed_commitment_by_hotkey(
1133+
netuid=netuid, hotkey_ss58_address=hotkey_ss58_address, block=block
1134+
)
10961135

10971136
async def get_all_revealed_commitments(
10981137
self,
10991138
netuid: int,
11001139
block: Optional[int] = None,
11011140
block_hash: Optional[str] = None,
11021141
reuse_block: bool = False,
1103-
) -> dict[str, tuple[int, str]]:
1142+
) -> dict[str, tuple[tuple[int, str], ...]]:
11041143
"""Returns all revealed commitments for a given netuid.
11051144
11061145
Arguments:
@@ -1111,6 +1150,12 @@ async def get_all_revealed_commitments(
11111150
11121151
Returns:
11131152
result (dict): A dictionary of all revealed commitments in view {ss58_address: (reveal block, commitment message)}.
1153+
1154+
Example of result:
1155+
{
1156+
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY": ( (12, "Alice message 1"), (152, "Alice message 2") ),
1157+
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty": ( (12, "Bob message 1"), (147, "Bob message 2") ),
1158+
}
11141159
"""
11151160
query = await self.query_map(
11161161
module="Commitments",
@@ -1122,8 +1167,11 @@ async def get_all_revealed_commitments(
11221167
)
11231168

11241169
result = {}
1125-
async for item in query:
1126-
result.update(decode_revealed_commitment_with_hotkey(item))
1170+
async for pair in query:
1171+
hotkey_ss58_address, commitment_message = (
1172+
decode_revealed_commitment_with_hotkey(pair)
1173+
)
1174+
result[hotkey_ss58_address] = commitment_message
11271175
return result
11281176

11291177
async def get_current_weight_commit_info(
@@ -1639,6 +1687,38 @@ async def get_neuron_for_pubkey_and_subnet(
16391687
reuse_block=reuse_block,
16401688
)
16411689

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+
16421722
async def get_owned_hotkeys(
16431723
self,
16441724
coldkey_ss58: str,
@@ -3764,6 +3844,8 @@ async def set_weights(
37643844
wait_for_inclusion: bool = False,
37653845
wait_for_finalization: bool = False,
37663846
max_retries: int = 5,
3847+
block_time: float = 12.0,
3848+
period: int = 5,
37673849
):
37683850
"""
37693851
Sets the inter-neuronal weights for the specified neuron. This process involves specifying the influence or
@@ -3783,6 +3865,8 @@ async def set_weights(
37833865
wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is
37843866
``False``.
37853867
max_retries (int): The number of maximum attempts to set weights. Default is ``5``.
3868+
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.
37863870
37873871
Returns:
37883872
tuple[bool, str]: ``True`` if the setting of weights is successful, False otherwise. And `msg`, a string
@@ -3831,6 +3915,7 @@ async def _blocks_weight_limit() -> bool:
38313915
version_key=version_key,
38323916
wait_for_inclusion=wait_for_inclusion,
38333917
wait_for_finalization=wait_for_finalization,
3918+
block_time=block_time,
38343919
)
38353920
retries += 1
38363921
return success, message
@@ -3856,6 +3941,7 @@ async def _blocks_weight_limit() -> bool:
38563941
version_key=version_key,
38573942
wait_for_inclusion=wait_for_inclusion,
38583943
wait_for_finalization=wait_for_finalization,
3944+
period=period,
38593945
)
38603946
except Exception as e:
38613947
logging.error(f"Error setting weights: {e}")

bittensor/core/chain_data/utils.py

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from bittensor.utils.balance import Balance
1212

1313
if TYPE_CHECKING:
14-
from async_substrate_interface.types import ScaleObj
14+
from async_substrate_interface.sync_substrate import QueryMapResult
1515

1616

1717
class ChainDataType(Enum):
@@ -140,12 +140,16 @@ def decode_metadata(metadata: dict) -> str:
140140
return bytes(bytes_tuple).decode()
141141

142142

143-
def decode_revealed_commitment(
144-
value: Optional["ScaleObj"],
145-
) -> Optional[tuple[int, str]]:
146-
"""Decode the revealed commitment data from the given input if it is not None."""
147-
if value is None:
148-
return None
143+
def decode_revealed_commitment(encoded_data) -> tuple[int, str]:
144+
"""
145+
Decode the revealed commitment data from the given input if it is not None.
146+
147+
Arguments:
148+
encoded_data (tuple[bytes, int]): A tuple containing the revealed message and the block number.
149+
150+
Returns:
151+
tuple[int, str]: A tuple containing the revealed block number and decoded commitment message.
152+
"""
149153

150154
def scale_decode_offset(data: bytes) -> int:
151155
"""Decodes the scale offset from a given byte data sequence."""
@@ -158,16 +162,25 @@ def scale_decode_offset(data: bytes) -> int:
158162
else:
159163
return 4
160164

161-
v = next(iter(value))
162-
revealed_block = v[1]
163-
cut = scale_decode_offset(v[0])
165+
com_bytes, revealed_block = encoded_data
166+
offset = scale_decode_offset(com_bytes)
164167

165-
revealed_commitment = bytes(v[0][cut:]).decode("utf-8", errors="ignore")
168+
revealed_commitment = bytes(com_bytes[offset:]).decode("utf-8", errors="ignore")
166169
return revealed_block, revealed_commitment
167170

168171

169-
def decode_revealed_commitment_with_hotkey(data: list) -> dict[str, tuple[int, str]]:
170-
"""Decode revealed commitment using a hotkey."""
171-
key, value = data
172-
ss58_address = decode_account_id(key[0])
173-
return {ss58_address: decode_revealed_commitment(value)}
172+
def decode_revealed_commitment_with_hotkey(
173+
encoded_data: "QueryMapResult",
174+
) -> tuple[str, tuple[tuple[int, str], ...]]:
175+
"""
176+
Decode revealed commitment using a hotkey.
177+
178+
Returns:
179+
tuple[str, tuple[tuple[int, str], ...]]: A tuple containing the hotkey (ss58 address) and a tuple of block
180+
numbers and their corresponding revealed commitments.
181+
"""
182+
key, data = encoded_data
183+
184+
ss58_address = decode_account_id(next(iter(key)))
185+
block_data = tuple(decode_revealed_commitment(p) for p in data.value)
186+
return ss58_address, block_data

0 commit comments

Comments
 (0)