|
18 | 18 | import time
|
19 | 19 | from typing import Union, Optional, TYPE_CHECKING
|
20 | 20 |
|
| 21 | +from bittensor_wallet.errors import KeyFileError |
21 | 22 | from retry import retry
|
22 | 23 | from rich.prompt import Confirm
|
23 | 24 |
|
@@ -285,3 +286,166 @@ def register_extrinsic(
|
285 | 286 | # Failed to register after max attempts.
|
286 | 287 | bt_console.print("[red]No more attempts.[/red]")
|
287 | 288 | return False
|
| 289 | + |
| 290 | + |
| 291 | +@ensure_connected |
| 292 | +def _do_burned_register( |
| 293 | + self, |
| 294 | + netuid: int, |
| 295 | + wallet: "Wallet", |
| 296 | + wait_for_inclusion: bool = False, |
| 297 | + wait_for_finalization: bool = True, |
| 298 | +) -> tuple[bool, Optional[str]]: |
| 299 | + """ |
| 300 | + Performs a burned register extrinsic call to the Subtensor chain. |
| 301 | +
|
| 302 | + This method sends a registration transaction to the Subtensor blockchain using the burned register mechanism. It |
| 303 | + retries the call up to three times with exponential backoff in case of failures. |
| 304 | +
|
| 305 | + Args: |
| 306 | + self (bittensor.core.subtensor.Subtensor): Subtensor instance. |
| 307 | + netuid (int): The network unique identifier to register on. |
| 308 | + wallet (bittensor_wallet.Wallet): The wallet to be registered. |
| 309 | + wait_for_inclusion (bool): Whether to wait for the transaction to be included in a block. Default is False. |
| 310 | + wait_for_finalization (bool): Whether to wait for the transaction to be finalized. Default is True. |
| 311 | +
|
| 312 | + Returns: |
| 313 | + Tuple[bool, Optional[str]]: A tuple containing a boolean indicating success or failure, and an optional error message. |
| 314 | + """ |
| 315 | + |
| 316 | + @retry(delay=1, tries=3, backoff=2, max_delay=4) |
| 317 | + def make_substrate_call_with_retry(): |
| 318 | + # create extrinsic call |
| 319 | + call = self.substrate.compose_call( |
| 320 | + call_module="SubtensorModule", |
| 321 | + call_function="burned_register", |
| 322 | + call_params={ |
| 323 | + "netuid": netuid, |
| 324 | + "hotkey": wallet.hotkey.ss58_address, |
| 325 | + }, |
| 326 | + ) |
| 327 | + extrinsic = self.substrate.create_signed_extrinsic( |
| 328 | + call=call, keypair=wallet.coldkey |
| 329 | + ) |
| 330 | + response = self.substrate.submit_extrinsic( |
| 331 | + extrinsic, |
| 332 | + wait_for_inclusion=wait_for_inclusion, |
| 333 | + wait_for_finalization=wait_for_finalization, |
| 334 | + ) |
| 335 | + |
| 336 | + # We only wait here if we expect finalization. |
| 337 | + if not wait_for_finalization and not wait_for_inclusion: |
| 338 | + return True, None |
| 339 | + |
| 340 | + # process if registration successful, try again if pow is still valid |
| 341 | + response.process_events() |
| 342 | + if not response.is_success: |
| 343 | + return False, format_error_message(response.error_message) |
| 344 | + # Successful registration |
| 345 | + else: |
| 346 | + return True, None |
| 347 | + |
| 348 | + return make_substrate_call_with_retry() |
| 349 | + |
| 350 | + |
| 351 | +def burned_register_extrinsic( |
| 352 | + subtensor: "Subtensor", |
| 353 | + wallet: "Wallet", |
| 354 | + netuid: int, |
| 355 | + wait_for_inclusion: bool = False, |
| 356 | + wait_for_finalization: bool = True, |
| 357 | + prompt: bool = False, |
| 358 | +) -> bool: |
| 359 | + """Registers the wallet to chain by recycling TAO. |
| 360 | +
|
| 361 | + Args: |
| 362 | + subtensor (bittensor.core.subtensor.Subtensor): Subtensor instance. |
| 363 | + wallet (bittensor.wallet): Bittensor wallet object. |
| 364 | + netuid (int): The ``netuid`` of the subnet to register on. |
| 365 | + 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. |
| 366 | + 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. |
| 367 | + prompt (bool): If ``true``, the call waits for confirmation from the user before proceeding. |
| 368 | +
|
| 369 | + Returns: |
| 370 | + 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``. |
| 371 | + """ |
| 372 | + if not subtensor.subnet_exists(netuid): |
| 373 | + bt_console.print( |
| 374 | + ":cross_mark: [red]Failed[/red]: error: [bold white]subnet:{}[/bold white] does not exist.".format( |
| 375 | + netuid |
| 376 | + ) |
| 377 | + ) |
| 378 | + return False |
| 379 | + |
| 380 | + try: |
| 381 | + wallet.unlock_coldkey() |
| 382 | + except KeyFileError: |
| 383 | + bt_console.print( |
| 384 | + ":cross_mark: [red]Keyfile is corrupt, non-writable, non-readable or the password used to decrypt is invalid[/red]:[bold white]\n [/bold white]" |
| 385 | + ) |
| 386 | + return False |
| 387 | + with bt_console.status( |
| 388 | + f":satellite: Checking Account on [bold]subnet:{netuid}[/bold]..." |
| 389 | + ): |
| 390 | + neuron = subtensor.get_neuron_for_pubkey_and_subnet( |
| 391 | + wallet.hotkey.ss58_address, netuid=netuid |
| 392 | + ) |
| 393 | + |
| 394 | + old_balance = subtensor.get_balance(wallet.coldkeypub.ss58_address) |
| 395 | + |
| 396 | + recycle_amount = subtensor.recycle(netuid=netuid) |
| 397 | + if not neuron.is_null: |
| 398 | + bt_console.print( |
| 399 | + ":white_heavy_check_mark: [green]Already Registered[/green]:\n" |
| 400 | + "uid: [bold white]{}[/bold white]\n" |
| 401 | + "netuid: [bold white]{}[/bold white]\n" |
| 402 | + "hotkey: [bold white]{}[/bold white]\n" |
| 403 | + "coldkey: [bold white]{}[/bold white]".format( |
| 404 | + neuron.uid, neuron.netuid, neuron.hotkey, neuron.coldkey |
| 405 | + ) |
| 406 | + ) |
| 407 | + return True |
| 408 | + |
| 409 | + if prompt: |
| 410 | + # Prompt user for confirmation. |
| 411 | + if not Confirm.ask(f"Recycle {recycle_amount} to register on subnet:{netuid}?"): |
| 412 | + return False |
| 413 | + |
| 414 | + with bt_console.status(":satellite: Recycling TAO for Registration..."): |
| 415 | + success, err_msg = _do_burned_register( |
| 416 | + self=subtensor, |
| 417 | + netuid=netuid, |
| 418 | + wallet=wallet, |
| 419 | + wait_for_inclusion=wait_for_inclusion, |
| 420 | + wait_for_finalization=wait_for_finalization, |
| 421 | + ) |
| 422 | + |
| 423 | + if not success: |
| 424 | + bt_console.print(f":cross_mark: [red]Failed[/red]: {err_msg}") |
| 425 | + time.sleep(0.5) |
| 426 | + return False |
| 427 | + # Successful registration, final check for neuron and pubkey |
| 428 | + else: |
| 429 | + bt_console.print(":satellite: Checking Balance...") |
| 430 | + block = subtensor.get_current_block() |
| 431 | + new_balance = subtensor.get_balance( |
| 432 | + wallet.coldkeypub.ss58_address, block=block |
| 433 | + ) |
| 434 | + |
| 435 | + bt_console.print( |
| 436 | + "Balance:\n [blue]{}[/blue] :arrow_right: [green]{}[/green]".format( |
| 437 | + old_balance, new_balance |
| 438 | + ) |
| 439 | + ) |
| 440 | + is_registered = subtensor.is_hotkey_registered( |
| 441 | + netuid=netuid, hotkey_ss58=wallet.hotkey.ss58_address |
| 442 | + ) |
| 443 | + if is_registered: |
| 444 | + bt_console.print(":white_heavy_check_mark: [green]Registered[/green]") |
| 445 | + return True |
| 446 | + else: |
| 447 | + # neuron not found, try again |
| 448 | + bt_console.print( |
| 449 | + ":cross_mark: [red]Unknown error. Neuron not found.[/red]" |
| 450 | + ) |
| 451 | + return False |
0 commit comments