Skip to content

Commit a1712e4

Browse files
committed
Adds burned_register to AsyncSubtensor
1 parent 4a8a546 commit a1712e4

File tree

2 files changed

+186
-3
lines changed

2 files changed

+186
-3
lines changed

bittensor/core/async_subtensor.py

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@
3030
DynamicInfo,
3131
)
3232
from bittensor.core.errors import StakeError
33-
from bittensor.core.extrinsics.async_registration import register_extrinsic
33+
from bittensor.core.extrinsics.async_registration import (
34+
register_extrinsic,
35+
burned_register_extrinsic,
36+
)
3437
from bittensor.core.extrinsics.async_root import (
3538
set_root_weights_extrinsic,
3639
root_register_extrinsic,
@@ -1224,6 +1227,33 @@ async def neurons_lite(
12241227

12251228
return NeuronInfoLite.list_from_vec_u8(hex_to_bytes(hex_bytes_result))
12261229

1230+
async def burned_register(
1231+
self,
1232+
wallet: "Wallet",
1233+
netuid: int,
1234+
wait_for_inclusion: bool = False,
1235+
wait_for_finalization: bool = True,
1236+
) -> bool:
1237+
"""
1238+
Registers a neuron on the Bittensor network by recycling TAO. This method of registration involves recycling TAO tokens, allowing them to be re-mined by performing work on the network.
1239+
1240+
Args:
1241+
wallet (bittensor_wallet.Wallet): The wallet associated with the neuron to be registered.
1242+
netuid (int): The unique identifier of the subnet.
1243+
wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. Defaults to `False`.
1244+
wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. Defaults to `True`.
1245+
1246+
Returns:
1247+
bool: ``True`` if the registration is successful, False otherwise.
1248+
"""
1249+
return await burned_register_extrinsic(
1250+
subtensor=self,
1251+
wallet=wallet,
1252+
netuid=netuid,
1253+
wait_for_inclusion=wait_for_inclusion,
1254+
wait_for_finalization=wait_for_finalization,
1255+
)
1256+
12271257
async def get_neuron_for_pubkey_and_subnet(
12281258
self,
12291259
hotkey_ss58: str,
@@ -1255,7 +1285,7 @@ async def get_neuron_for_pubkey_and_subnet(
12551285
if uid is None:
12561286
return NeuronInfo.get_null_neuron()
12571287

1258-
params = [netuid, uid]
1288+
params = [netuid, uid.value]
12591289
json_body = await self.substrate.rpc_request(
12601290
method="neuronInfo_getNeuron",
12611291
params=params,
@@ -1761,6 +1791,22 @@ async def weights_rate_limit(
17611791
)
17621792
return None if call is None else int(call)
17631793

1794+
async def recycle(self, netuid: int) -> Optional["Balance"]:
1795+
"""
1796+
Retrieves the 'Burn' hyperparameter for a specified subnet. The 'Burn' parameter represents the amount of Tao that is effectively recycled within the Bittensor network.
1797+
1798+
Args:
1799+
netuid (int): The unique identifier of the subnet.
1800+
block (Optional[int]): The blockchain block number for the query.
1801+
1802+
Returns:
1803+
Optional[Balance]: The value of the 'Burn' hyperparameter if the subnet exists, None otherwise.
1804+
1805+
Understanding the 'Burn' rate is essential for analyzing the network registration usage, particularly how it is correlated with user activity and the overall cost of participation in a given subnet.
1806+
"""
1807+
call = await self.get_hyperparameter(param_name="Burn", netuid=netuid)
1808+
return None if call is None else Balance.from_rao(int(call.value))
1809+
17641810
async def blocks_since_last_update(self, netuid: int, uid: int) -> Optional[int]:
17651811
"""
17661812
Returns the number of blocks since the last update for a specific UID in the subnetwork.

bittensor/core/extrinsics/async_registration.py

Lines changed: 138 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
from bittensor_wallet import Wallet
1313

14-
from bittensor.utils import format_error_message
14+
from bittensor.utils import format_error_message, unlock_key
1515
from bittensor.utils.btlogging import logging
1616
from bittensor.utils.registration import log_no_torch_error, create_pow_async
1717

@@ -263,3 +263,140 @@ async def register_extrinsic(
263263
# Failed to register after max attempts.
264264
logging.error("[red]No more attempts.[/red]")
265265
return False
266+
267+
268+
async def _do_burned_register(
269+
subtensor: "AsyncSubtensor",
270+
netuid: int,
271+
wallet: "Wallet",
272+
wait_for_inclusion: bool = False,
273+
wait_for_finalization: bool = True,
274+
) -> tuple[bool, Optional[str]]:
275+
"""
276+
Performs a burned register extrinsic call to the Subtensor chain.
277+
278+
This method sends a registration transaction to the Subtensor blockchain using the burned register mechanism.
279+
280+
Args:
281+
subtensor (bittensor.core.async_subtensor.AsyncSubtensor): AsyncSubtensor instance.
282+
netuid (int): The network unique identifier to register on.
283+
wallet (bittensor_wallet.Wallet): The wallet to be registered.
284+
wait_for_inclusion (bool): Whether to wait for the transaction to be included in a block. Default is False.
285+
wait_for_finalization (bool): Whether to wait for the transaction to be finalized. Default is True.
286+
287+
Returns:
288+
Tuple[bool, Optional[str]]: A tuple containing a boolean indicating success or failure, and an optional error message.
289+
"""
290+
291+
# create extrinsic call
292+
call = await subtensor.substrate.compose_call(
293+
call_module="SubtensorModule",
294+
call_function="burned_register",
295+
call_params={
296+
"netuid": netuid,
297+
"hotkey": wallet.hotkey.ss58_address,
298+
},
299+
)
300+
extrinsic = await subtensor.substrate.create_signed_extrinsic(
301+
call=call, keypair=wallet.coldkey
302+
)
303+
response = await subtensor.substrate.submit_extrinsic(
304+
extrinsic=extrinsic,
305+
wait_for_inclusion=wait_for_inclusion,
306+
wait_for_finalization=wait_for_finalization,
307+
)
308+
309+
# We only wait here if we expect finalization.
310+
if not wait_for_finalization and not wait_for_inclusion:
311+
return True, None
312+
313+
# process if registration successful, try again if pow is still valid
314+
await response.process_events()
315+
if not await response.is_success:
316+
return False, format_error_message(error_message=await response.error_message)
317+
# Successful registration
318+
else:
319+
return True, None
320+
321+
322+
async def burned_register_extrinsic(
323+
subtensor: "AsyncSubtensor",
324+
wallet: "Wallet",
325+
netuid: int,
326+
wait_for_inclusion: bool = False,
327+
wait_for_finalization: bool = True,
328+
) -> bool:
329+
"""Registers the wallet to chain by recycling TAO.
330+
331+
Args:
332+
subtensor (bittensor.core.async_subtensor.AsyncSubtensor): AsyncSubtensor instance.
333+
wallet (bittensor.wallet): Bittensor wallet object.
334+
netuid (int): The ``netuid`` of the subnet to register on.
335+
wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning ``true``, or returns ``false`` if the extrinsic fails to enter the block within the timeout.
336+
wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning ``true``, or returns ``false`` if the extrinsic fails to be finalized within the timeout.
337+
338+
Returns:
339+
success (bool): Flag is ``true`` if extrinsic was finalized or uncluded in the block. If we did not wait for finalization / inclusion, the response is ``true``.
340+
"""
341+
if not await subtensor.subnet_exists(netuid):
342+
logging.error(
343+
f":cross_mark: [red]Failed error:[/red] subnet [blue]{netuid}[/blue] does not exist."
344+
)
345+
return False
346+
347+
if not (unlock := unlock_key(wallet)).success:
348+
logging.error(unlock.message)
349+
return False
350+
351+
logging.info(
352+
f":satellite: [magenta]Checking Account on subnet[/magenta] [blue]{netuid}[/blue][magenta] ...[/magenta]"
353+
)
354+
neuron = await subtensor.get_neuron_for_pubkey_and_subnet(
355+
wallet.hotkey.ss58_address, netuid=netuid
356+
)
357+
358+
old_balance = await subtensor.get_balance(wallet.coldkeypub.ss58_address)
359+
360+
if not neuron.is_null:
361+
logging.info(":white_heavy_check_mark: [green]Already Registered[/green]")
362+
logging.info(f"\t\tuid: [blue]{neuron.uid}[/blue]")
363+
logging.info(f"\t\tnetuid: [blue]{neuron.netuid}[/blue]")
364+
logging.info(f"\t\thotkey: [blue]{neuron.hotkey}[/blue]")
365+
logging.info(f"\t\tcoldkey: [blue]{neuron.coldkey}[/blue]")
366+
return True
367+
368+
logging.info(":satellite: [magenta]Recycling TAO for Registration...[/magenta]")
369+
370+
recycle_amount = await subtensor.recycle(netuid=netuid)
371+
logging.info(f"Recycling {recycle_amount} to register on subnet:{netuid}")
372+
373+
success, err_msg = await _do_burned_register(
374+
subtensor=subtensor,
375+
netuid=netuid,
376+
wallet=wallet,
377+
wait_for_inclusion=wait_for_inclusion,
378+
wait_for_finalization=wait_for_finalization,
379+
)
380+
381+
if not success:
382+
logging.error(f":cross_mark: [red]Failed error:[/red] {err_msg}")
383+
await asyncio.sleep(0.5)
384+
return False
385+
# Successful registration, final check for neuron and pubkey
386+
else:
387+
logging.info(":satellite: [magenta]Checking Balance...[/magenta]")
388+
new_balance = await subtensor.get_balance(wallet.coldkeypub.ss58_address)
389+
390+
logging.info(
391+
f"Balance: [blue]{old_balance}[/blue] :arrow_right: [green]{new_balance}[/green]"
392+
)
393+
is_registered = await subtensor.is_hotkey_registered(
394+
netuid=netuid, hotkey_ss58=wallet.hotkey.ss58_address
395+
)
396+
if is_registered:
397+
logging.info(":white_heavy_check_mark: [green]Registered[/green]")
398+
return True
399+
else:
400+
# neuron not found, try again
401+
logging.error(":cross_mark: [red]Unknown error. Neuron not found.[/red]")
402+
return False

0 commit comments

Comments
 (0)