Skip to content

Commit d2eec00

Browse files
authored
Merge pull request #2828 from opentensor/feat/roman/start-call-extrinsic
Add `start_call` extrinsic to the sdk
2 parents a10be9e + 39bc79b commit d2eec00

File tree

9 files changed

+324
-11
lines changed

9 files changed

+324
-11
lines changed

bittensor/core/async_subtensor.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
publish_metadata,
5858
get_metadata,
5959
)
60+
from bittensor.core.extrinsics.asyncex.start_call import start_call_extrinsic
6061
from bittensor.core.extrinsics.asyncex.serving import serve_axon_extrinsic
6162
from bittensor.core.extrinsics.asyncex.staking import (
6263
add_stake_extrinsic,
@@ -3986,6 +3987,36 @@ async def serve_axon(
39863987
certificate=certificate,
39873988
)
39883989

3990+
async def start_call(
3991+
self,
3992+
wallet: "Wallet",
3993+
netuid: int,
3994+
wait_for_inclusion: bool = True,
3995+
wait_for_finalization: bool = False,
3996+
) -> tuple[bool, str]:
3997+
"""
3998+
Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start a
3999+
new subnet's emission mechanism).
4000+
4001+
Args:
4002+
wallet (Wallet): The wallet used to sign the extrinsic (must be unlocked).
4003+
netuid (int): The UID of the target subnet for which the call is being initiated.
4004+
wait_for_inclusion (bool, optional): Whether to wait for the extrinsic to be included in a block. Defaults to True.
4005+
wait_for_finalization (bool, optional): Whether to wait for finalization of the extrinsic. Defaults to False.
4006+
4007+
Returns:
4008+
Tuple[bool, str]:
4009+
- True and a success message if the extrinsic is successfully submitted or processed.
4010+
- False and an error message if the submission fails or the wallet cannot be unlocked.
4011+
"""
4012+
return await start_call_extrinsic(
4013+
subtensor=self,
4014+
wallet=wallet,
4015+
netuid=netuid,
4016+
wait_for_inclusion=wait_for_inclusion,
4017+
wait_for_finalization=wait_for_finalization,
4018+
)
4019+
39894020
async def swap_stake(
39904021
self,
39914022
wallet: "Wallet",
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from typing import TYPE_CHECKING
2+
3+
from bittensor.utils import unlock_key
4+
from bittensor.utils.btlogging import logging
5+
6+
if TYPE_CHECKING:
7+
from bittensor_wallet import Wallet
8+
from bittensor.core.async_subtensor import AsyncSubtensor
9+
10+
11+
async def start_call_extrinsic(
12+
subtensor: "AsyncSubtensor",
13+
wallet: "Wallet",
14+
netuid: int,
15+
wait_for_inclusion: bool = True,
16+
wait_for_finalization: bool = False,
17+
) -> tuple[bool, str]:
18+
"""
19+
Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start a
20+
new subnet's emission mechanism).
21+
22+
Args:
23+
subtensor (Subtensor): The Subtensor client instance used for blockchain interaction.
24+
wallet (Wallet): The wallet used to sign the extrinsic (must be unlocked).
25+
netuid (int): The UID of the target subnet for which the call is being initiated.
26+
wait_for_inclusion (bool, optional): Whether to wait for the extrinsic to be included in a block. Defaults to True.
27+
wait_for_finalization (bool, optional): Whether to wait for finalization of the extrinsic. Defaults to False.
28+
29+
Returns:
30+
Tuple[bool, str]:
31+
- True and a success message if the extrinsic is successfully submitted or processed.
32+
- False and an error message if the submission fails or the wallet cannot be unlocked.
33+
"""
34+
if not (unlock := unlock_key(wallet)).success:
35+
logging.error(unlock.message)
36+
return False, unlock.message
37+
38+
async with subtensor.substrate as substrate:
39+
start_call = await substrate.compose_call(
40+
call_module="SubtensorModule",
41+
call_function="start_call",
42+
call_params={"netuid": netuid},
43+
)
44+
signed_ext = await substrate.create_signed_extrinsic(
45+
call=start_call,
46+
keypair=wallet.coldkey,
47+
)
48+
49+
response = await substrate.submit_extrinsic(
50+
extrinsic=signed_ext,
51+
wait_for_inclusion=wait_for_inclusion,
52+
wait_for_finalization=wait_for_finalization,
53+
)
54+
55+
if not wait_for_finalization and not wait_for_inclusion:
56+
return True, "Not waiting for finalization or inclusion."
57+
58+
if await response.is_success:
59+
return True, "Success with `start_call` response."
60+
61+
return False, await response.error_message
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
from typing import TYPE_CHECKING
2+
3+
from bittensor.utils import unlock_key
4+
from bittensor.utils.btlogging import logging
5+
6+
if TYPE_CHECKING:
7+
from bittensor_wallet import Wallet
8+
from bittensor.core.subtensor import Subtensor
9+
10+
11+
def start_call_extrinsic(
12+
subtensor: "Subtensor",
13+
wallet: "Wallet",
14+
netuid: int,
15+
wait_for_inclusion: bool = True,
16+
wait_for_finalization: bool = False,
17+
) -> tuple[bool, str]:
18+
"""
19+
Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start a
20+
new subnet's emission mechanism).
21+
22+
Args:
23+
subtensor (Subtensor): The Subtensor client instance used for blockchain interaction.
24+
wallet (Wallet): The wallet used to sign the extrinsic (must be unlocked).
25+
netuid (int): The UID of the target subnet for which the call is being initiated.
26+
wait_for_inclusion (bool, optional): Whether to wait for the extrinsic to be included in a block. Defaults to True.
27+
wait_for_finalization (bool, optional): Whether to wait for finalization of the extrinsic. Defaults to False.
28+
29+
Returns:
30+
Tuple[bool, str]:
31+
- True and a success message if the extrinsic is successfully submitted or processed.
32+
- False and an error message if the submission fails or the wallet cannot be unlocked.
33+
"""
34+
if not (unlock := unlock_key(wallet)).success:
35+
logging.error(unlock.message)
36+
return False, unlock.message
37+
38+
with subtensor.substrate as substrate:
39+
start_call = substrate.compose_call(
40+
call_module="SubtensorModule",
41+
call_function="start_call",
42+
call_params={"netuid": netuid},
43+
)
44+
45+
signed_ext = substrate.create_signed_extrinsic(
46+
call=start_call,
47+
keypair=wallet.coldkey,
48+
)
49+
50+
response = substrate.submit_extrinsic(
51+
extrinsic=signed_ext,
52+
wait_for_inclusion=wait_for_inclusion,
53+
wait_for_finalization=wait_for_finalization,
54+
)
55+
56+
if not wait_for_finalization and not wait_for_inclusion:
57+
return True, "Not waiting for finalization or inclusion."
58+
59+
if response.is_success:
60+
return True, "Success with `start_call` response."
61+
62+
return False, response.error_message

bittensor/core/subtensor.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
get_metadata,
6161
serve_axon_extrinsic,
6262
)
63+
from bittensor.core.extrinsics.start_call import start_call_extrinsic
6364
from bittensor.core.extrinsics.set_weights import set_weights_extrinsic
6465
from bittensor.core.extrinsics.staking import (
6566
add_stake_extrinsic,
@@ -3252,6 +3253,36 @@ def serve_axon(
32523253
certificate=certificate,
32533254
)
32543255

3256+
def start_call(
3257+
self,
3258+
wallet: "Wallet",
3259+
netuid: int,
3260+
wait_for_inclusion: bool = True,
3261+
wait_for_finalization: bool = False,
3262+
) -> tuple[bool, str]:
3263+
"""
3264+
Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start a
3265+
new subnet's emission mechanism).
3266+
3267+
Args:
3268+
wallet (Wallet): The wallet used to sign the extrinsic (must be unlocked).
3269+
netuid (int): The UID of the target subnet for which the call is being initiated.
3270+
wait_for_inclusion (bool, optional): Whether to wait for the extrinsic to be included in a block. Defaults to True.
3271+
wait_for_finalization (bool, optional): Whether to wait for finalization of the extrinsic. Defaults to False.
3272+
3273+
Returns:
3274+
Tuple[bool, str]:
3275+
- True and a success message if the extrinsic is successfully submitted or processed.
3276+
- False and an error message if the submission fails or the wallet cannot be unlocked.
3277+
"""
3278+
return start_call_extrinsic(
3279+
subtensor=self,
3280+
wallet=wallet,
3281+
netuid=netuid,
3282+
wait_for_inclusion=wait_for_inclusion,
3283+
wait_for_finalization=wait_for_finalization,
3284+
)
3285+
32553286
def swap_stake(
32563287
self,
32573288
wallet: "Wallet",

tests/e2e_tests/test_incentive.py

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,10 @@
33
import pytest
44

55
from bittensor.utils.btlogging import logging
6-
76
from tests.e2e_tests.utils.chain_interactions import (
87
root_set_subtensor_hyperparameter_values,
98
sudo_set_admin_utils,
109
wait_epoch,
11-
wait_interval,
1210
)
1311

1412
DURATION_OF_START_CALL = 10
@@ -70,15 +68,8 @@ async def test_incentive(local_chain, subtensor, templates, alice_wallet, bob_wa
7068

7169
subtensor.wait_for_block(DURATION_OF_START_CALL)
7270

73-
# Subnet "Start Call" https://github.com/opentensor/bits/pull/13
74-
status, error = await root_set_subtensor_hyperparameter_values(
75-
local_chain,
76-
alice_wallet,
77-
call_function="start_call",
78-
call_params={
79-
"netuid": netuid,
80-
},
81-
)
71+
# # Subnet "Start Call" https://github.com/opentensor/bits/pull/13
72+
status, error = subtensor.start_call(wallet=alice_wallet, netuid=netuid)
8273

8374
assert status is True, error
8475

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from bittensor.core.extrinsics.asyncex import start_call
2+
import pytest
3+
4+
5+
@pytest.mark.asyncio
6+
async def test_start_call_extrinsics(subtensor, mocker, fake_wallet):
7+
"""Test that start_call_extrinsic correctly constructs and submits the extrinsic."""
8+
9+
# Preps
10+
netuid = 123
11+
wallet = fake_wallet
12+
wallet.name = "fake_wallet"
13+
wallet.coldkey = "fake_coldkey"
14+
15+
substrate = subtensor.substrate.__aenter__.return_value
16+
substrate.compose_call.return_value = "mock_call"
17+
substrate.create_signed_extrinsic.return_value = "signed_ext"
18+
substrate.submit_extrinsic.return_value = mocker.MagicMock(
19+
is_success=mocker.AsyncMock(return_value=True)(), error_message=""
20+
)
21+
22+
# Call
23+
success, message = await start_call.start_call_extrinsic(
24+
subtensor=subtensor,
25+
wallet=wallet,
26+
netuid=netuid,
27+
)
28+
29+
# Assertions
30+
substrate.compose_call.assert_awaited_once_with(
31+
call_module="SubtensorModule",
32+
call_function="start_call",
33+
call_params={"netuid": netuid},
34+
)
35+
36+
substrate.create_signed_extrinsic.assert_awaited_once_with(
37+
call="mock_call",
38+
keypair=wallet.coldkey,
39+
)
40+
41+
substrate.submit_extrinsic.assert_awaited_once_with(
42+
extrinsic="signed_ext",
43+
wait_for_inclusion=True,
44+
wait_for_finalization=False,
45+
)
46+
47+
assert success is True
48+
assert "Success" in message
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from bittensor.core.extrinsics import start_call
2+
3+
4+
def test_start_call_extrinsics(subtensor, mocker, fake_wallet):
5+
"""Test that start_call_extrinsic correctly constructs and submits the extrinsic."""
6+
7+
# Preps
8+
netuid = 123
9+
wallet = fake_wallet
10+
wallet.name = "fake_wallet"
11+
wallet.coldkey = "fake_coldkey"
12+
13+
substrate = subtensor.substrate.__enter__.return_value
14+
substrate.compose_call.return_value = "mock_call"
15+
substrate.create_signed_extrinsic.return_value = "signed_ext"
16+
substrate.submit_extrinsic.return_value = mocker.MagicMock(
17+
is_success=True, error_message=""
18+
)
19+
20+
# Call
21+
success, message = start_call.start_call_extrinsic(
22+
subtensor=subtensor,
23+
wallet=wallet,
24+
netuid=netuid,
25+
)
26+
27+
# Assertions
28+
substrate.compose_call.assert_called_once_with(
29+
call_module="SubtensorModule",
30+
call_function="start_call",
31+
call_params={"netuid": netuid},
32+
)
33+
34+
substrate.create_signed_extrinsic.assert_called_once_with(
35+
call="mock_call",
36+
keypair=wallet.coldkey,
37+
)
38+
39+
substrate.submit_extrinsic.assert_called_once_with(
40+
extrinsic="signed_ext",
41+
wait_for_inclusion=True,
42+
wait_for_finalization=False,
43+
)
44+
45+
assert success is True
46+
assert "Success" in message

tests/unit_tests/test_async_subtensor.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3013,3 +3013,25 @@ async def test_get_owned_hotkeys_return_empty(subtensor, mocker):
30133013
reuse_block_hash=False,
30143014
)
30153015
assert result == []
3016+
3017+
3018+
@pytest.mark.asyncio
3019+
async def test_start_call(subtensor, mocker):
3020+
"""Test start_call extrinsic calls properly."""
3021+
# preps
3022+
wallet_name = mocker.Mock(spec=Wallet)
3023+
netuid = 123
3024+
mocked_extrinsic = mocker.patch.object(async_subtensor, "start_call_extrinsic")
3025+
3026+
# Call
3027+
result = await subtensor.start_call(wallet_name, netuid)
3028+
3029+
# Asserts
3030+
mocked_extrinsic.assert_awaited_once_with(
3031+
subtensor=subtensor,
3032+
wallet=wallet_name,
3033+
netuid=netuid,
3034+
wait_for_inclusion=True,
3035+
wait_for_finalization=False,
3036+
)
3037+
assert result == mocked_extrinsic.return_value

tests/unit_tests/test_subtensor.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3388,3 +3388,24 @@ def test_get_owned_hotkeys_return_empty(subtensor, mocker):
33883388
reuse_block_hash=False,
33893389
)
33903390
assert result == []
3391+
3392+
3393+
def test_start_call(subtensor, mocker):
3394+
"""Test start_call extrinsic calls properly."""
3395+
# preps
3396+
wallet_name = mocker.Mock(spec=Wallet)
3397+
netuid = 123
3398+
mocked_extrinsic = mocker.patch.object(subtensor_module, "start_call_extrinsic")
3399+
3400+
# Call
3401+
result = subtensor.start_call(wallet_name, netuid)
3402+
3403+
# Asserts
3404+
mocked_extrinsic.assert_called_once_with(
3405+
subtensor=subtensor,
3406+
wallet=wallet_name,
3407+
netuid=netuid,
3408+
wait_for_inclusion=True,
3409+
wait_for_finalization=False,
3410+
)
3411+
assert result == mocked_extrinsic.return_value

0 commit comments

Comments
 (0)