Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 50 additions & 50 deletions bittensor/core/async_subtensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@
)
from bittensor.core.extrinsics.asyncex.registration import (
burned_register_extrinsic,
register_extrinsic,
register_limit_extrinsic,
register_subnet_extrinsic,
set_subnet_identity_extrinsic,
Expand Down Expand Up @@ -7818,14 +7817,7 @@ async def register(
self: "AsyncSubtensor",
wallet: "Wallet",
netuid: int,
max_allowed_attempts: int = 3,
output_in_place: bool = False,
cuda: bool = False,
dev_id: Union[list[int], int] = 0,
tpb: int = 256,
num_processes: Optional[int] = None,
update_interval: Optional[int] = None,
log_verbose: bool = False,
limit_price: Optional[Balance] = None,
*,
mev_protection: bool = DEFAULT_MEV_PROTECTION,
period: Optional[int] = DEFAULT_PERIOD,
Expand All @@ -7834,63 +7826,71 @@ async def register(
wait_for_finalization: bool = True,
wait_for_revealed_execution: bool = True,
) -> ExtrinsicResponse:
"""
Registers a neuron on the Bittensor subnet with provided netuid using the provided wallet.
"""Registers a neuron on the Bittensor network by recycling TAO, with automatic price protection.

Uses ``register_limit`` under the hood. If ``limit_price`` is not provided, it is automatically
calculated as the current recycle (burn) cost plus a 0.5% tolerance to protect against price fluctuations.

Registration is a critical step for a neuron to become an active participant in the network, enabling it to
stake, set weights, and receive incentives.
For root subnet (``netuid == 0``), delegates to ``root_register_extrinsic``.

Parameters:
wallet: The wallet associated with the neuron to be registered.
netuid: The unique identifier of the subnet.
max_allowed_attempts: Maximum number of attempts to register the wallet.
output_in_place: If `True`, prints the progress of the proof of work to the console in-place. Meaning the
progress is printed on the same lines.
cuda: If `true`, the wallet should be registered using CUDA device(s).
dev_id: The CUDA device id to use, or a list of device ids.
tpb: The number of threads per block (CUDA).
num_processes: The number of processes to use to register.
update_interval: The number of nonces to solve between updates.
log_verbose: If `true`, the registration process will log more information.
mev_protection: If `True`, encrypts and submits the transaction through the MEV Shield pallet to protect
limit_price: Maximum acceptable burn price as a Balance instance. If ``None``, automatically calculated
as ``recycle * 1.005`` (0.5% tolerance). If the on-chain burn price exceeds this value, the
transaction will fail with RegistrationPriceLimitExceeded.
mev_protection: If ``True``, encrypts and submits the transaction through the MEV Shield pallet to protect
against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators
decrypt and execute it. If `False`, submits the transaction directly without encryption.
decrypt and execute it. If ``False``, submits the transaction directly without encryption.
period: The number of blocks during which the transaction will remain valid after it's submitted. If the
transaction is not included in a block within that number of blocks, it will expire and be rejected. You
can think of it as an expiration date for the transaction.
raise_error: Raises a relevant exception rather than returning `False` if unsuccessful.
wait_for_inclusion: Whether to wait for the inclusion of the transaction.
wait_for_finalization: Whether to wait for the finalization of the transaction.
raise_error: Raises a relevant exception rather than returning ``False`` if unsuccessful.
wait_for_inclusion: Waits for the transaction to be included in a block.
wait_for_finalization: Waits for the transaction to be finalized on the blockchain.
wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used.

Returns:
ExtrinsicResponse: The result object of the extrinsic execution.

This function facilitates the entry of new neurons into the network, supporting the decentralized growth and
scalability of the Bittensor ecosystem.

Notes:
- Rate Limits: <https://docs.learnbittensor.org/learn/chain-rate-limits#registration-rate-limits>
"""
return await register_extrinsic(
subtensor=self,
wallet=wallet,
netuid=netuid,
max_allowed_attempts=max_allowed_attempts,
tpb=tpb,
update_interval=update_interval,
num_processes=num_processes,
cuda=cuda,
dev_id=dev_id,
output_in_place=output_in_place,
log_verbose=log_verbose,
mev_protection=mev_protection,
period=period,
raise_error=raise_error,
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
wait_for_revealed_execution=wait_for_revealed_execution,
)
async with self:
if netuid == 0:
return await root_register_extrinsic(
subtensor=self,
wallet=wallet,
mev_protection=mev_protection,
period=period,
raise_error=raise_error,
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
wait_for_revealed_execution=wait_for_revealed_execution,
)

if limit_price is not None:
check_balance_amount(limit_price)
else:
recycle = await self.recycle(netuid=netuid)
if recycle is None:
return ExtrinsicResponse(
False, f"Subnet {netuid} does not exist."
).with_log()
limit_price = Balance.from_rao(recycle.rao * 1005 // 1000)

return await register_limit_extrinsic(
subtensor=self,
wallet=wallet,
netuid=netuid,
limit_price=limit_price,
mev_protection=mev_protection,
period=period,
raise_error=raise_error,
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
wait_for_revealed_execution=wait_for_revealed_execution,
)

async def register_limit(
self,
Expand Down Expand Up @@ -7950,7 +7950,7 @@ async def register_limit(
)

async def register_subnet(
self: "AsyncSubtensor",
self,
wallet: "Wallet",
*,
mev_protection: bool = DEFAULT_MEV_PROTECTION,
Expand Down
211 changes: 2 additions & 209 deletions bittensor/core/extrinsics/asyncex/registration.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"""
This module provides async functionalities for registering a wallet with the subtensor network using Proof-of-Work (PoW).
This module provides async functionalities for registering a wallet with the subtensor network.
"""

import asyncio
from typing import Optional, Union, TYPE_CHECKING
from typing import Optional, TYPE_CHECKING

from bittensor.core.errors import BalanceTypeError, RegistrationError
from bittensor.core.extrinsics.asyncex.mev_shield import submit_encrypted_extrinsic
Expand All @@ -12,7 +12,6 @@
from bittensor.core.types import ExtrinsicResponse
from bittensor.utils.balance import Balance
from bittensor.utils.btlogging import logging
from bittensor.utils.registration import create_pow_async, log_no_torch_error, torch

if TYPE_CHECKING:
from bittensor_wallet import Wallet
Expand Down Expand Up @@ -392,212 +391,6 @@ async def register_subnet_extrinsic(
return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error)


async def register_extrinsic(
subtensor: "AsyncSubtensor",
wallet: "Wallet",
netuid: int,
max_allowed_attempts: int = 3,
output_in_place: bool = True,
cuda: bool = False,
dev_id: Union[list[int], int] = 0,
tpb: int = 256,
num_processes: Optional[int] = None,
update_interval: Optional[int] = None,
log_verbose: bool = False,
*,
mev_protection: bool = DEFAULT_MEV_PROTECTION,
period: Optional[int] = None,
raise_error: bool = False,
wait_for_inclusion: bool = True,
wait_for_finalization: bool = True,
wait_for_revealed_execution: bool = True,
) -> ExtrinsicResponse:
"""Registers a neuron on the Bittensor subnet with provided netuid using the provided wallet.

Registration is a critical step for a neuron to become an active participant in the network, enabling it to stake,
set weights, and receive incentives.

Parameters:
subtensor: Subtensor object to use for chain interactions
wallet: Bittensor wallet object.
netuid: The ``netuid`` of the subnet to register on.
max_allowed_attempts: Maximum number of attempts to register the wallet.
output_in_place: Whether the POW solving should be outputted to the console as it goes along.
cuda: If `True`, the wallet should be registered using CUDA device(s).
dev_id: The CUDA device id to use, or a list of device ids.
tpb: The number of threads per block (CUDA).
num_processes: The number of processes to use to register.
update_interval: The number of nonces to solve between updates.
log_verbose: If `True`, the registration process will log more information.
mev_protection: If True, encrypts and submits the transaction through the MEV Shield pallet to protect
against front-running and MEV attacks. The transaction remains encrypted in the mempool until validators
decrypt and execute it. If False, submits the transaction directly without encryption.
period: The number of blocks during which the transaction will remain valid after it's submitted. If the
transaction is not included in a block within that number of blocks, it will expire and be rejected. You can
think of it as an expiration date for the transaction.
raise_error: Raises a relevant exception rather than returning `False` if unsuccessful.
wait_for_inclusion: Whether to wait for the inclusion of the transaction.
wait_for_finalization: Whether to wait for the finalization of the transaction.
wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used.

Returns:
ExtrinsicResponse: The result object of the extrinsic execution.
"""
try:
if not (
unlocked := ExtrinsicResponse.unlock_wallet(
wallet, raise_error, unlock_type="both"
)
).success:
return unlocked

block_hash = await subtensor.substrate.get_chain_head()
if not await subtensor.subnet_exists(netuid, block_hash=block_hash):
return ExtrinsicResponse(
False, f"Subnet {netuid} does not exist."
).with_log()

neuron = await subtensor.get_neuron_for_pubkey_and_subnet(
hotkey_ss58=wallet.hotkey.ss58_address, netuid=netuid, block_hash=block_hash
)

if not neuron.is_null:
message = "Already registered."
logging.debug(f"[green]{message}[/green]")
logging.debug(f"\t\tuid: [blue]{neuron.uid}[/blue]")
logging.debug(f"\t\tnetuid: [blue]{neuron.netuid}[/blue]")
logging.debug(f"\t\thotkey: [blue]{neuron.hotkey}[/blue]")
logging.debug(f"\t\tcoldkey: [blue]{neuron.coldkey}[/blue]")
return ExtrinsicResponse(message=message, data={"neuron": neuron})

logging.debug(
f"Registration hotkey: [blue]{wallet.hotkey.ss58_address}[/blue], Public coldkey: "
f"[blue]{wallet.coldkey.ss58_address}[/blue] in the network: [blue]{subtensor.network}[/blue]."
)

if not torch:
log_no_torch_error()
return ExtrinsicResponse(False, "Torch is not installed.").with_log()

# Attempt rolling registration.
attempts = 1

while True:
# Solve latest POW.
if cuda:
if not torch.cuda.is_available():
return ExtrinsicResponse(False, "CUDA not available.").with_log()

logging.debug("Creating a POW with CUDA.")
pow_result = await create_pow_async(
subtensor=subtensor,
wallet=wallet,
netuid=netuid,
output_in_place=output_in_place,
cuda=cuda,
dev_id=dev_id,
tpb=tpb,
num_processes=num_processes,
update_interval=update_interval,
log_verbose=log_verbose,
)
else:
logging.debug("Creating a POW.")
pow_result = await create_pow_async(
subtensor=subtensor,
wallet=wallet,
netuid=netuid,
output_in_place=output_in_place,
cuda=cuda,
num_processes=num_processes,
update_interval=update_interval,
log_verbose=log_verbose,
)

# pow failed
if not pow_result:
# might be registered already on this subnet
is_registered = await subtensor.is_hotkey_registered(
netuid=netuid, hotkey_ss58=wallet.hotkey.ss58_address
)
if is_registered:
message = f"Already registered in subnet {netuid}."
logging.debug(f"[green]{message}[/green]")
return ExtrinsicResponse(message=message)

# pow successful, proceed to submit pow to chain for registration
else:
# check if a pow result is still valid
while not await pow_result.is_stale_async(subtensor=subtensor):
call = await SubtensorModule(subtensor).register(
netuid=netuid,
coldkey=wallet.coldkeypub.ss58_address,
hotkey=wallet.hotkey.ss58_address,
block_number=pow_result.block_number,
nonce=pow_result.nonce,
work=[int(byte_) for byte_ in pow_result.seal],
)
if mev_protection:
response = await submit_encrypted_extrinsic(
subtensor=subtensor,
wallet=wallet,
call=call,
period=period,
raise_error=raise_error,
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
wait_for_revealed_execution=wait_for_revealed_execution,
)
else:
response = await subtensor.sign_and_send_extrinsic(
call=call,
wallet=wallet,
period=period,
raise_error=raise_error,
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
)

if not response.success:
# Look error here
# https://github.com/opentensor/subtensor/blob/development/pallets/subtensor/src/errors.rs
if "HotKeyAlreadyRegisteredInSubNet" in response.message:
logging.debug(
f"[green]Already registered on subnet:[/green] [blue]{netuid}[/blue]."
)
return response
await asyncio.sleep(0.5)

if response.success:
is_registered = await subtensor.is_hotkey_registered(
netuid=netuid, hotkey_ss58=wallet.hotkey.ss58_address
)
if is_registered:
logging.debug("[green]Registered.[/green]")
return response

# neuron not found, try again
logging.warning("[red]Unknown error. Neuron not found.[/red]")
continue
else:
# Exited loop because pow is no longer valid.
logging.warning("[red]POW is stale.[/red]")
# Try again.

if attempts < max_allowed_attempts:
# Failed registration, retry pow
attempts += 1
logging.warning(
f"Failed registration, retrying pow ... [blue]({attempts}/{max_allowed_attempts})[/blue]"
)
else:
# Failed to register after max attempts.
return ExtrinsicResponse(False, "No more attempts.").with_log()

except Exception as error:
return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error)


async def set_subnet_identity_extrinsic(
subtensor: "AsyncSubtensor",
wallet: "Wallet",
Expand Down
Loading
Loading