Skip to content

Commit af5226e

Browse files
authored
Merge pull request #2958 from opentensor/bug/roman/make-dynamic-info-backwards-compatable
Make DynamicInfo backwards compatible
2 parents 97ccae7 + c9bc6e2 commit af5226e

File tree

5 files changed

+206
-36
lines changed

5 files changed

+206
-36
lines changed

bittensor/core/async_subtensor.py

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -799,15 +799,15 @@ async def all_subnets(
799799
subnets = await subtensor.all_subnets()
800800
"""
801801
block_hash = await self.determine_block_hash(
802-
block_number, block_hash, reuse_block
802+
block=block_number, block_hash=block_hash, reuse_block=reuse_block
803803
)
804804
if not block_hash and reuse_block:
805805
block_hash = self.substrate.last_block_hash
806806

807807
query, subnet_prices = await asyncio.gather(
808808
self.substrate.runtime_call(
809-
"SubnetInfoRuntimeApi",
810-
"get_all_dynamic_info",
809+
api="SubnetInfoRuntimeApi",
810+
method="get_all_dynamic_info",
811811
block_hash=block_hash,
812812
),
813813
self.get_subnet_prices(),
@@ -2616,9 +2616,7 @@ async def get_subnet_price(
26162616
if netuid == 0:
26172617
return Balance.from_tao(1)
26182618

2619-
block_hash = await self.determine_block_hash(
2620-
block=block, block_hash=block_hash, reuse_block=reuse_block
2621-
)
2619+
block_hash = await self.determine_block_hash(block=block)
26222620
current_sqrt_price = await self.substrate.query(
26232621
module="Swap",
26242622
storage_function="AlphaSqrtPrice",
@@ -3729,20 +3727,32 @@ async def subnet(
37293727
Returns:
37303728
Optional[DynamicInfo]: A DynamicInfo object, containing detailed information about a subnet.
37313729
"""
3732-
block_hash = await self.determine_block_hash(block, block_hash, reuse_block)
3730+
block_hash = await self.determine_block_hash(
3731+
block=block, block_hash=block_hash, reuse_block=reuse_block
3732+
)
37333733

37343734
if not block_hash and reuse_block:
37353735
block_hash = self.substrate.last_block_hash
37363736

3737-
query = await self.substrate.runtime_call(
3738-
"SubnetInfoRuntimeApi",
3739-
"get_dynamic_info",
3740-
params=[netuid],
3741-
block_hash=block_hash,
3737+
query, price = await asyncio.gather(
3738+
self.substrate.runtime_call(
3739+
"SubnetInfoRuntimeApi",
3740+
"get_dynamic_info",
3741+
params=[netuid],
3742+
block_hash=block_hash,
3743+
),
3744+
self.get_subnet_price(
3745+
netuid=netuid,
3746+
block=block,
3747+
block_hash=block_hash,
3748+
reuse_block=reuse_block,
3749+
),
3750+
return_exceptions=True,
37423751
)
37433752

37443753
if isinstance(decoded := query.decode(), dict):
3745-
price = self.get_subnet_price(netuid=netuid, block=block)
3754+
if isinstance(price, SubstrateRequestException):
3755+
price = None
37463756
return DynamicInfo.from_dict({**decoded, "price": price})
37473757
return None
37483758

bittensor/core/chain_data/dynamic_info.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -75,21 +75,24 @@ def _from_dict(cls, decoded: dict) -> "DynamicInfo":
7575

7676
subnet_volume = Balance.from_rao(decoded["subnet_volume"]).set_unit(netuid)
7777

78-
if decoded.get("subnet_identity"):
78+
if subnet_identity := decoded.get("subnet_identity"):
79+
# we need to check it for keep backwards compatibility
80+
logo_bytes = subnet_identity.get("logo_url")
81+
si_logo_url = bytes(logo_bytes).decode() if logo_bytes else None
82+
7983
subnet_identity = SubnetIdentity(
80-
subnet_name=bytes(decoded["subnet_identity"]["subnet_name"]).decode(),
81-
github_repo=bytes(decoded["subnet_identity"]["github_repo"]).decode(),
82-
subnet_contact=bytes(
83-
decoded["subnet_identity"]["subnet_contact"]
84-
).decode(),
85-
subnet_url=bytes(decoded["subnet_identity"]["subnet_url"]).decode(),
86-
logo_url=bytes(decoded["subnet_identity"]["logo_url"]).decode(),
87-
discord=bytes(decoded["subnet_identity"]["discord"]).decode(),
88-
description=bytes(decoded["subnet_identity"]["description"]).decode(),
89-
additional=bytes(decoded["subnet_identity"]["additional"]).decode(),
84+
subnet_name=bytes(subnet_identity["subnet_name"]).decode(),
85+
github_repo=bytes(subnet_identity["github_repo"]).decode(),
86+
subnet_contact=bytes(subnet_identity["subnet_contact"]).decode(),
87+
subnet_url=bytes(subnet_identity["subnet_url"]).decode(),
88+
logo_url=si_logo_url,
89+
discord=bytes(subnet_identity["discord"]).decode(),
90+
description=bytes(subnet_identity["description"]).decode(),
91+
additional=bytes(subnet_identity["additional"]).decode(),
9092
)
9193
else:
9294
subnet_identity = None
95+
9396
price = decoded.get("price", None)
9497

9598
if price and not isinstance(price, Balance):
@@ -110,7 +113,11 @@ def _from_dict(cls, decoded: dict) -> "DynamicInfo":
110113
tao_in=tao_in,
111114
k=tao_in.rao * alpha_in.rao,
112115
is_dynamic=is_dynamic,
113-
price=price,
116+
price=(
117+
price
118+
if price is not None
119+
else Balance.from_tao(tao_in.tao / alpha_in.tao).set_unit(netuid)
120+
),
114121
alpha_out_emission=alpha_out_emission,
115122
alpha_in_emission=alpha_in_emission,
116123
tao_in_emission=tao_in_emission,

bittensor/core/subtensor.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -445,15 +445,14 @@ def all_subnets(self, block: Optional[int] = None) -> Optional[list["DynamicInfo
445445
Optional[DynamicInfo]: A list of DynamicInfo objects, each containing detailed information about a subnet.
446446
447447
"""
448-
block_hash = self.determine_block_hash(block)
448+
block_hash = self.determine_block_hash(block=block)
449449
query = self.substrate.runtime_call(
450-
"SubnetInfoRuntimeApi",
451-
"get_all_dynamic_info",
450+
api="SubnetInfoRuntimeApi",
451+
method="get_all_dynamic_info",
452452
block_hash=block_hash,
453453
)
454454
subnet_prices = self.get_subnet_prices()
455455
decoded = query.decode()
456-
457456
for sn in decoded:
458457
sn.update({"price": subnet_prices.get(sn["netuid"], Balance.from_tao(0))})
459458
return DynamicInfo.list_from_dicts(decoded)
@@ -2696,17 +2695,20 @@ def subnet(self, netuid: int, block: Optional[int] = None) -> Optional[DynamicIn
26962695
Optional[DynamicInfo]: A DynamicInfo object, containing detailed information about a subnet.
26972696
26982697
"""
2699-
block_hash = self.determine_block_hash(block)
2698+
block_hash = self.determine_block_hash(block=block)
27002699

27012700
query = self.substrate.runtime_call(
2702-
"SubnetInfoRuntimeApi",
2703-
"get_dynamic_info",
2701+
api="SubnetInfoRuntimeApi",
2702+
method="get_dynamic_info",
27042703
params=[netuid],
27052704
block_hash=block_hash,
27062705
)
27072706

27082707
if isinstance(decoded := query.decode(), dict):
2709-
price = self.get_subnet_price(netuid=netuid, block=block)
2708+
try:
2709+
price = self.get_subnet_price(netuid=netuid, block=block)
2710+
except SubstrateRequestException:
2711+
price = None
27102712
return DynamicInfo.from_dict({**decoded, "price": price})
27112713
return None
27122714

tests/unit_tests/test_async_subtensor.py

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3833,9 +3833,7 @@ async def test_get_subnet_price(subtensor, mocker):
38333833
)
38343834

38353835
# Asserts
3836-
mocked_determine_block_hash.assert_awaited_once_with(
3837-
block=None, block_hash=None, reuse_block=False
3838-
)
3836+
mocked_determine_block_hash.assert_awaited_once_with(block=None)
38393837
mocked_query.assert_awaited_once_with(
38403838
module="Swap",
38413839
storage_function="AlphaSqrtPrice",
@@ -3875,3 +3873,85 @@ async def fake_current_sqrt_prices():
38753873
page_size=129, # total number of subnets
38763874
)
38773875
assert result == expected_prices
3876+
3877+
3878+
@pytest.mark.asyncio
3879+
async def test_all_subnets(subtensor, mocker):
3880+
"""Verify that `all_subnets` calls proper methods and returns the correct value."""
3881+
# Preps
3882+
mocked_determine_block_hash = mocker.patch.object(subtensor, "determine_block_hash")
3883+
mocked_di_list_from_dicts = mocker.patch.object(
3884+
async_subtensor.DynamicInfo, "list_from_dicts"
3885+
)
3886+
mocked_get_subnet_prices = mocker.patch.object(
3887+
subtensor,
3888+
"get_subnet_prices",
3889+
return_value={0: Balance.from_tao(1), 1: Balance.from_tao(0.029258617)},
3890+
)
3891+
mocked_decode = mocker.Mock(return_value=[{"netuid": 0}, {"netuid": 1}])
3892+
mocked_runtime_call = mocker.Mock(decode=mocked_decode)
3893+
mocker.patch.object(
3894+
subtensor.substrate, "runtime_call", return_value=mocked_runtime_call
3895+
)
3896+
3897+
# Call
3898+
result = await subtensor.all_subnets()
3899+
3900+
# Asserts
3901+
mocked_determine_block_hash.assert_awaited_once_with(
3902+
block=None, block_hash=None, reuse_block=False
3903+
)
3904+
subtensor.substrate.runtime_call.assert_called_once_with(
3905+
api="SubnetInfoRuntimeApi",
3906+
method="get_all_dynamic_info",
3907+
block_hash=mocked_determine_block_hash.return_value,
3908+
)
3909+
mocked_get_subnet_prices.assert_called_once()
3910+
mocked_di_list_from_dicts.assert_called_once_with(
3911+
[
3912+
{"netuid": 0, "price": Balance.from_tao(1)},
3913+
{"netuid": 1, "price": Balance.from_tao(0.029258617)},
3914+
]
3915+
)
3916+
assert result == mocked_di_list_from_dicts.return_value
3917+
3918+
3919+
@pytest.mark.asyncio
3920+
async def test_subnet(subtensor, mocker):
3921+
"""Verify that `subnet` calls proper methods and returns the correct value."""
3922+
# Preps
3923+
netuid = 14
3924+
mocked_determine_block_hash = mocker.patch.object(subtensor, "determine_block_hash")
3925+
mocked_di_from_dict = mocker.patch.object(async_subtensor.DynamicInfo, "from_dict")
3926+
mocked_get_subnet_price = mocker.patch.object(
3927+
subtensor, "get_subnet_price", return_value=Balance.from_tao(100.0)
3928+
)
3929+
mocked_decode = mocker.Mock(return_value={"netuid": netuid})
3930+
mocked_runtime_call = mocker.Mock(decode=mocked_decode)
3931+
mocker.patch.object(
3932+
subtensor.substrate, "runtime_call", return_value=mocked_runtime_call
3933+
)
3934+
3935+
# Call
3936+
result = await subtensor.subnet(netuid=netuid)
3937+
3938+
# Asserts
3939+
mocked_determine_block_hash.assert_awaited_once_with(
3940+
block=None, block_hash=None, reuse_block=False
3941+
)
3942+
subtensor.substrate.runtime_call.assert_awaited_once_with(
3943+
"SubnetInfoRuntimeApi",
3944+
"get_dynamic_info",
3945+
params=[netuid],
3946+
block_hash=mocked_determine_block_hash.return_value,
3947+
)
3948+
mocked_get_subnet_price.assert_awaited_once_with(
3949+
netuid=netuid,
3950+
block=None,
3951+
block_hash=mocked_determine_block_hash.return_value,
3952+
reuse_block=False,
3953+
)
3954+
mocked_di_from_dict.assert_called_once_with(
3955+
{"netuid": netuid, "price": Balance.from_tao(100.0)}
3956+
)
3957+
assert result == mocked_di_from_dict.return_value

tests/unit_tests/test_subtensor.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4195,3 +4195,74 @@ def test_get_subnet_prices(subtensor, mocker):
41954195
page_size=129, # total number of subnets
41964196
)
41974197
assert result == expected_prices
4198+
4199+
4200+
def test_all_subnets(subtensor, mocker):
4201+
"""Verify that `all_subnets` calls proper methods and returns the correct value."""
4202+
# Preps
4203+
mocked_determine_block_hash = mocker.patch.object(subtensor, "determine_block_hash")
4204+
mocked_di_list_from_dicts = mocker.patch.object(
4205+
subtensor_module.DynamicInfo, "list_from_dicts"
4206+
)
4207+
mocked_get_subnet_prices = mocker.patch.object(
4208+
subtensor,
4209+
"get_subnet_prices",
4210+
return_value={0: Balance.from_tao(1), 1: Balance.from_tao(0.029258617)},
4211+
)
4212+
mocked_decode = mocker.Mock(return_value=[{"netuid": 0}, {"netuid": 1}])
4213+
mocked_runtime_call = mocker.Mock(decode=mocked_decode)
4214+
mocker.patch.object(
4215+
subtensor.substrate, "runtime_call", return_value=mocked_runtime_call
4216+
)
4217+
4218+
# Call
4219+
result = subtensor.all_subnets()
4220+
4221+
# Asserts
4222+
mocked_determine_block_hash.assert_called_once_with(block=None)
4223+
subtensor.substrate.runtime_call.assert_called_once_with(
4224+
api="SubnetInfoRuntimeApi",
4225+
method="get_all_dynamic_info",
4226+
block_hash=mocked_determine_block_hash.return_value,
4227+
)
4228+
mocked_get_subnet_prices.assert_called_once()
4229+
mocked_di_list_from_dicts.assert_called_once_with(
4230+
[
4231+
{"netuid": 0, "price": Balance.from_tao(1)},
4232+
{"netuid": 1, "price": Balance.from_tao(0.029258617)},
4233+
]
4234+
)
4235+
assert result == mocked_di_list_from_dicts.return_value
4236+
4237+
4238+
def test_subnet(subtensor, mocker):
4239+
"""Verify that `subnet` calls proper methods and returns the correct value."""
4240+
# Preps
4241+
netuid = 14
4242+
mocked_determine_block_hash = mocker.patch.object(subtensor, "determine_block_hash")
4243+
mocked_di_from_dict = mocker.patch.object(subtensor_module.DynamicInfo, "from_dict")
4244+
mocked_get_subnet_price = mocker.patch.object(
4245+
subtensor, "get_subnet_price", return_value=Balance.from_tao(100.0)
4246+
)
4247+
mocked_decode = mocker.Mock(return_value={"netuid": netuid})
4248+
mocked_runtime_call = mocker.Mock(decode=mocked_decode)
4249+
mocker.patch.object(
4250+
subtensor.substrate, "runtime_call", return_value=mocked_runtime_call
4251+
)
4252+
4253+
# Call
4254+
result = subtensor.subnet(netuid=netuid)
4255+
4256+
# Asserts
4257+
subtensor.substrate.runtime_call.assert_called_once_with(
4258+
api="SubnetInfoRuntimeApi",
4259+
method="get_dynamic_info",
4260+
params=[netuid],
4261+
block_hash=mocked_determine_block_hash.return_value,
4262+
)
4263+
mocked_determine_block_hash.assert_called_once_with(block=None)
4264+
mocked_get_subnet_price.assert_called_once_with(netuid=netuid, block=None)
4265+
mocked_di_from_dict.assert_called_once_with(
4266+
{"netuid": netuid, "price": Balance.from_tao(100.0)}
4267+
)
4268+
assert result == mocked_di_from_dict.return_value

0 commit comments

Comments
 (0)