From b4919ec9d9149f6add7e1ef57e4e4367f86224c5 Mon Sep 17 00:00:00 2001 From: Gus Date: Mon, 20 May 2024 13:40:03 -0400 Subject: [PATCH 1/9] feat: Add setting delegate take --- bittensor/cli.py | 2 + bittensor/commands/__init__.py | 1 + bittensor/commands/delegates.py | 125 ++++++++++++++- bittensor/errors.py | 6 + bittensor/extrinsics/delegation.py | 142 +++++++++++++++++- bittensor/subtensor.py | 142 +++++++++++++++++- tests/e2e_tests/__init__.py | 0 tests/e2e_tests/conftest.py | 57 +++++++ tests/e2e_tests/multistep/__init__.py | 0 .../e2e_tests/multistep/test_last_tx_block.py | 51 +++++++ tests/e2e_tests/subcommands/__init__.py | 0 .../subcommands/delegation/__init__.py | 0 .../delegation/test_set_delegate_take.py | 56 +++++++ .../e2e_tests/subcommands/wallet/__init__.py | 0 .../subcommands/wallet/test_transfer.py | 32 ++++ tests/e2e_tests/utils.py | 32 ++++ 16 files changed, 636 insertions(+), 10 deletions(-) create mode 100644 tests/e2e_tests/__init__.py create mode 100644 tests/e2e_tests/conftest.py create mode 100644 tests/e2e_tests/multistep/__init__.py create mode 100644 tests/e2e_tests/multistep/test_last_tx_block.py create mode 100644 tests/e2e_tests/subcommands/__init__.py create mode 100644 tests/e2e_tests/subcommands/delegation/__init__.py create mode 100644 tests/e2e_tests/subcommands/delegation/test_set_delegate_take.py create mode 100644 tests/e2e_tests/subcommands/wallet/__init__.py create mode 100644 tests/e2e_tests/subcommands/wallet/test_transfer.py create mode 100644 tests/e2e_tests/utils.py diff --git a/bittensor/cli.py b/bittensor/cli.py index b1b544b906..0f708aca79 100644 --- a/bittensor/cli.py +++ b/bittensor/cli.py @@ -51,6 +51,7 @@ RunFaucetCommand, SenateCommand, SetIdentityCommand, + SetTakeCommand, StakeCommand, StakeShow, SubnetGetHyperparamsCommand, @@ -121,6 +122,7 @@ "senate": SenateCommand, "register": RootRegisterCommand, "proposals": ProposalsCommand, + "set_take": SetTakeCommand, "delegate": DelegateStakeCommand, "undelegate": DelegateUnstakeCommand, "my_delegates": MyDelegatesCommand, diff --git a/bittensor/commands/__init__.py b/bittensor/commands/__init__.py index 7e52f0a1ed..fb37044638 100644 --- a/bittensor/commands/__init__.py +++ b/bittensor/commands/__init__.py @@ -77,6 +77,7 @@ DelegateStakeCommand, DelegateUnstakeCommand, MyDelegatesCommand, + SetTakeCommand, ) from .wallets import ( NewColdkeyCommand, diff --git a/bittensor/commands/delegates.py b/bittensor/commands/delegates.py index 1dcacd9149..25ea195d5e 100644 --- a/bittensor/commands/delegates.py +++ b/bittensor/commands/delegates.py @@ -21,8 +21,7 @@ import bittensor from typing import List, Optional from rich.table import Table -from rich.prompt import Prompt -from rich.prompt import Confirm +from rich.prompt import Confirm, FloatPrompt, Prompt from rich.console import Text from tqdm import tqdm from substrateinterface.exceptions import SubstrateRequestException @@ -102,9 +101,9 @@ def show_delegates( for prev_delegate in prev_delegates: prev_delegates_dict[prev_delegate.hotkey_ss58] = prev_delegate - registered_delegate_info: Optional[ - Dict[str, DelegatesDetails] - ] = get_delegates_details(url=bittensor.__delegates_details_url__) + registered_delegate_info: Optional[Dict[str, DelegatesDetails]] = ( + get_delegates_details(url=bittensor.__delegates_details_url__) + ) if registered_delegate_info is None: bittensor.__console__.print( ":warning:[yellow]Could not get delegate info from chain.[/yellow]" @@ -808,9 +807,9 @@ def _run(cli: "bittensor.cli", subtensor: "bittensor.subtensor"): delegates.sort(key=lambda delegate: delegate[0].total_stake, reverse=True) total_delegated += sum(my_delegates.values()) - registered_delegate_info: Optional[ - DelegatesDetails - ] = get_delegates_details(url=bittensor.__delegates_details_url__) + registered_delegate_info: Optional[DelegatesDetails] = ( + get_delegates_details(url=bittensor.__delegates_details_url__) + ) if registered_delegate_info is None: bittensor.__console__.print( ":warning:[yellow]Could not get delegate info from chain.[/yellow]" @@ -891,3 +890,113 @@ def check_config(config: "bittensor.config"): ): wallet_name = Prompt.ask("Enter wallet name", default=defaults.wallet.name) config.wallet.name = str(wallet_name) + + +class SetTakeCommand: + """ + Executes the ``set_take`` command, which sets the delegate take. + The command performs several checks: + 1. Hotkey is already a delegate + 2. New take value is within 0-18% range + Optional Arguments: + - ``take``: The new take value + - ``wallet.name``: The name of the wallet to use for the command. + - ``wallet.hotkey``: The name of the hotkey to use for the command. + Usage: + To run the command, the user must have a configured wallet with both hotkey and coldkey. Also, the hotkey should already be a delegate. + Example usage:: + btcli root set_take --wallet.name my_wallet --wallet.hotkey my_hotkey + Note: + This function can be used to update the takes individually for every subnet + """ + + @staticmethod + def run(cli: "bittensor.cli"): + r"""Set delegate take.""" + try: + subtensor: "bittensor.subtensor" = bittensor.subtensor( + config=cli.config, log_verbose=False + ) + SetTakeCommand._run(cli, subtensor) + finally: + if "subtensor" in locals(): + subtensor.close() + bittensor.logging.debug("closing subtensor connection") + + @staticmethod + def _run(cli: "bittensor.cli", subtensor: "bittensor.subtensor"): + r"""Set delegate take.""" + config = cli.config.copy() + wallet = bittensor.wallet(config=cli.config) + + # Unlock the wallet. + wallet.hotkey + wallet.coldkey + + # Check if the hotkey is not a delegate. + if not subtensor.is_hotkey_delegate(wallet.hotkey.ss58_address): + bittensor.__console__.print( + "Aborting: Hotkey {} is NOT a delegate.".format( + wallet.hotkey.ss58_address + ) + ) + return + + # Prompt user for take value. + new_take_str = config.get("take") + if new_take_str == None: + new_take = FloatPrompt.ask(f"Enter take value (0.18 for 18%)") + else: + new_take = float(new_take_str) + + if new_take > 0.18: + bittensor.__console__.print( + "ERROR: Take value should be in the range of 0 to 18%" + ) + return + + result: bool = subtensor.set_take( + wallet=wallet, + delegate_ss58=wallet.hotkey.ss58_address, + take=new_take, + ) + if not result: + bittensor.__console__.print("Could not set the take") + else: + # Check if we are a delegate. + is_delegate: bool = subtensor.is_hotkey_delegate(wallet.hotkey.ss58_address) + if not is_delegate: + bittensor.__console__.print( + "Could not set the take [white]{}[/white]".format(subtensor.network) + ) + return + bittensor.__console__.print( + "Successfully set the take on [white]{}[/white]".format( + subtensor.network + ) + ) + + @staticmethod + def add_args(parser: argparse.ArgumentParser): + set_take_parser = parser.add_parser( + "set_take", help="""Set take for delegate""" + ) + set_take_parser.add_argument( + "--take", + dest="take", + type=float, + required=False, + help="""Take as a float number""", + ) + bittensor.wallet.add_args(set_take_parser) + bittensor.subtensor.add_args(set_take_parser) + + @staticmethod + def check_config(config: "bittensor.config"): + if not config.is_set("wallet.name") and not config.no_prompt: + wallet_name = Prompt.ask("Enter wallet name", default=defaults.wallet.name) + config.wallet.name = str(wallet_name) + + if not config.is_set("wallet.hotkey") and not config.no_prompt: + hotkey = Prompt.ask("Enter hotkey name", default=defaults.wallet.hotkey) + config.wallet.hotkey = str(hotkey) diff --git a/bittensor/errors.py b/bittensor/errors.py index 13cb43fc1b..de51b5d48a 100644 --- a/bittensor/errors.py +++ b/bittensor/errors.py @@ -64,6 +64,12 @@ class NominationError(ChainTransactionError): pass +class TakeError(ChainTransactionError): + r"""Error raised when a increase / decrease take transaction fails.""" + + pass + + class TransferError(ChainTransactionError): r"""Error raised when a transfer transaction fails.""" diff --git a/bittensor/extrinsics/delegation.py b/bittensor/extrinsics/delegation.py index 0b3bdb7d64..f568f63998 100644 --- a/bittensor/extrinsics/delegation.py +++ b/bittensor/extrinsics/delegation.py @@ -18,7 +18,13 @@ import logging import bittensor -from ..errors import NominationError, NotDelegateError, NotRegisteredError, StakeError +from ..errors import ( + NominationError, + NotDelegateError, + NotRegisteredError, + StakeError, + TakeError, +) from rich.prompt import Confirm from typing import Union, Optional from bittensor.utils.balance import Balance @@ -357,3 +363,137 @@ def undelegate_extrinsic( except StakeError as e: bittensor.__console__.print(":cross_mark: [red]Stake Error: {}[/red]".format(e)) return False + + +def decrease_take_extrinsic( + subtensor: "bittensor.subtensor", + wallet: "bittensor.wallet", + hotkey_ss58: Optional[str] = None, + take: int = 0, + wait_for_finalization: bool = False, + wait_for_inclusion: bool = True, +) -> bool: + r"""Decrease delegate take for the hotkey. + + Args: + wallet (bittensor.wallet): + Bittensor wallet object. + hotkey_ss58 (Optional[str]): + The ``ss58`` address of the hotkey account to stake to defaults to the wallet's hotkey. + take (float): + The ``take`` of the hotkey. + Returns: + success (bool): ``True`` if the transaction was successful. + """ + # Unlock the coldkey. + wallet.coldkey + wallet.hotkey + + with bittensor.__console__.status( + ":satellite: Sending decrease_take_extrinsic call on [white]{}[/white] ...".format( + subtensor.network + ) + ): + try: + success = subtensor._do_decrease_take( + wallet=wallet, + hotkey_ss58=hotkey_ss58, + take=take, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + + if success == True: + bittensor.__console__.print( + ":white_heavy_check_mark: [green]Finalized[/green]" + ) + bittensor.logging.success( + prefix="Decrease Delegate Take", + sufix="Finalized: " + str(success), + ) + + return success + + except Exception as e: + bittensor.__console__.print( + ":cross_mark: [red]Failed[/red]: error:{}".format(e) + ) + bittensor.logging.warning( + prefix="Set weights", sufix="Failed: " + str(e) + ) + except TakeError as e: + bittensor.__console__.print( + ":cross_mark: [red]Failed[/red]: error:{}".format(e) + ) + bittensor.logging.warning( + prefix="Set weights", sufix="Failed: " + str(e) + ) + + return False + + +def increase_take_extrinsic( + subtensor: "bittensor.subtensor", + wallet: "bittensor.wallet", + hotkey_ss58: Optional[str] = None, + take: int = 0, + wait_for_finalization: bool = False, + wait_for_inclusion: bool = True, +) -> bool: + r"""Increase delegate take for the hotkey. + + Args: + wallet (bittensor.wallet): + Bittensor wallet object. + hotkey_ss58 (Optional[str]): + The ``ss58`` address of the hotkey account to stake to defaults to the wallet's hotkey. + take (float): + The ``take`` of the hotkey. + Returns: + success (bool): ``True`` if the transaction was successful. + """ + # Unlock the coldkey. + wallet.coldkey + wallet.hotkey + + with bittensor.__console__.status( + ":satellite: Sending increase_take_extrinsic call on [white]{}[/white] ...".format( + subtensor.network + ) + ): + try: + success = subtensor._do_increase_take( + wallet=wallet, + hotkey_ss58=hotkey_ss58, + take=take, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + + if success == True: + bittensor.__console__.print( + ":white_heavy_check_mark: [green]Finalized[/green]" + ) + bittensor.logging.success( + prefix="Increase Delegate Take", + sufix="Finalized: " + str(success), + ) + + return success + + except Exception as e: + bittensor.__console__.print( + ":cross_mark: [red]Failed[/red]: error:{}".format(e) + ) + bittensor.logging.warning( + prefix="Set weights", sufix="Failed: " + str(e) + ) + except TakeError as e: + bittensor.__console__.print( + ":cross_mark: [red]Failed[/red]: error:{}".format(e) + ) + bittensor.logging.warning( + prefix="Set weights", sufix="Failed: " + str(e) + ) + + return False diff --git a/bittensor/subtensor.py b/bittensor/subtensor.py index 7ad6256833..3de3c0d3c9 100644 --- a/bittensor/subtensor.py +++ b/bittensor/subtensor.py @@ -47,7 +47,7 @@ IPInfo, custom_rpc_type_registry, ) -from .errors import IdentityError, NominationError, StakeError +from .errors import IdentityError, NominationError, StakeError, TakeError from .extrinsics.network import ( register_subnetwork_extrinsic, set_hyperparameter_extrinsic, @@ -73,6 +73,8 @@ delegate_extrinsic, nominate_extrinsic, undelegate_extrinsic, + increase_take_extrinsic, + decrease_take_extrinsic, ) from .extrinsics.senate import ( register_senate_extrinsic, @@ -542,6 +544,68 @@ def undelegate( prompt=prompt, ) + def set_take( + self, + wallet: "bittensor.wallet", + delegate_ss58: Optional[str] = None, + take: float = 0.0, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = False, + ) -> bool: + """ + Set delegate hotkey take + Args: + wallet (bittensor.wallet): The wallet containing the hotkey to be nominated. + delegate_ss58 (str, optional): Hotkey + take (float): Delegate take on subnet ID + wait_for_finalization (bool, optional): If ``True``, waits until the transaction is finalized on the blockchain. + wait_for_inclusion (bool, optional): If ``True``, waits until the transaction is included in a block. + Returns: + bool: ``True`` if the process is successful, False otherwise. + This function is a key part of the decentralized governance mechanism of Bittensor, allowing for the + dynamic selection and participation of validators in the network's consensus process. + """ + # Ensure delegate_ss58 is not None + if delegate_ss58 is None: + raise ValueError("delegate_ss58 cannot be None") + + # Caulate u16 representation of the take + takeu16 = int(take * 0xFFFF) + + # Check if the new take is greater or lower than existing take or if existing is set + delegate = self.get_delegate_by_hotkey(delegate_ss58) + current_take = None + if delegate is not None: + current_take = int(float(delegate.take) * 65535.0) + + if takeu16 == current_take: + bittensor.__console__.print("Nothing to do, take hasn't changed") + return True + if current_take is None or current_take < takeu16: + bittensor.__console__.print( + "Current take is either not set or is lower than the new one. Will use increase_take" + ) + return increase_take_extrinsic( + subtensor=self, + wallet=wallet, + hotkey_ss58=delegate_ss58, + take=takeu16, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + else: + bittensor.__console__.print( + "Current take is higher than the new one. Will use decrease_take" + ) + return decrease_take_extrinsic( + subtensor=self, + wallet=wallet, + hotkey_ss58=delegate_ss58, + take=takeu16, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + def send_extrinsic( self, wallet: "bittensor.wallet", @@ -4312,6 +4376,82 @@ def make_substrate_call_with_retry(): return make_substrate_call_with_retry() + def _do_increase_take( + self, + wallet: "bittensor.wallet", + hotkey_ss58: str, + take: int, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = False, + ) -> bool: + @retry(delay=2, tries=3, backoff=2, max_delay=4) + def make_substrate_call_with_retry(): + with self.substrate as substrate: + call = substrate.compose_call( + call_module="SubtensorModule", + call_function="increase_take", + call_params={ + "hotkey": hotkey_ss58, + "take": take, + }, + ) + extrinsic = substrate.create_signed_extrinsic( + call=call, keypair=wallet.coldkey + ) # sign with coldkey + response = substrate.submit_extrinsic( + extrinsic, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + # We only wait here if we expect finalization. + if not wait_for_finalization and not wait_for_inclusion: + return True + response.process_events() + if response.is_success: + return True + else: + raise TakeError(response.error_message) + + return make_substrate_call_with_retry() + + def _do_decrease_take( + self, + wallet: "bittensor.wallet", + hotkey_ss58: str, + take: int, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = False, + ) -> bool: + @retry(delay=2, tries=3, backoff=2, max_delay=4) + def make_substrate_call_with_retry(): + with self.substrate as substrate: + call = substrate.compose_call( + call_module="SubtensorModule", + call_function="decrease_take", + call_params={ + "hotkey": hotkey_ss58, + "take": take, + }, + ) + extrinsic = substrate.create_signed_extrinsic( + call=call, keypair=wallet.coldkey + ) # sign with coldkey + response = substrate.submit_extrinsic( + extrinsic, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + # We only wait here if we expect finalization. + if not wait_for_finalization and not wait_for_inclusion: + return True + response.process_events() + if response.is_success: + return True + else: + raise TakeError(response.error_message) + + return make_substrate_call_with_retry() + ################ #### Legacy #### ################ diff --git a/tests/e2e_tests/__init__.py b/tests/e2e_tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/e2e_tests/conftest.py b/tests/e2e_tests/conftest.py new file mode 100644 index 0000000000..2300eafc77 --- /dev/null +++ b/tests/e2e_tests/conftest.py @@ -0,0 +1,57 @@ +import os +import signal +from substrateinterface import SubstrateInterface +import pytest +import subprocess +import logging +import shlex +import re +import time + +logging.basicConfig(level=logging.INFO) + + +# Fixture for setting up and tearing down a localnet.sh chain between tests +@pytest.fixture(scope="function") +def local_chain(): + # Get the environment variable for the script path + script_path = os.getenv("LOCALNET_SH_PATH") + + if not script_path: + # Skip the test if the localhost.sh path is not set + logging.warning("LOCALNET_SH_PATH env variable is not set, e2e test skipped.") + pytest.skip("LOCALNET_SH_PATH environment variable is not set.") + + # Start new node process + cmds = shlex.split(script_path) + process = subprocess.Popen( + cmds, stdout=subprocess.PIPE, text=True, preexec_fn=os.setsid + ) + + # Pattern match indicates node is compiled and ready + pattern = re.compile(r"Successfully ran block step\.") + + def wait_for_node_start(process, pattern): + for line in process.stdout: + print(line.strip()) + if pattern.search(line): + print("Node started!") + break + + wait_for_node_start(process, pattern) + + # Run the test, passing in substrate interface + yield SubstrateInterface(url="ws://127.0.0.1:9945") + + # Terminate the process group (includes all child processes) + os.killpg(os.getpgid(process.pid), signal.SIGTERM) + + # Give some time for the process to terminate + time.sleep(1) + + # If the process is not terminated, send SIGKILL + if process.poll() is None: + os.killpg(os.getpgid(process.pid), signal.SIGKILL) + + # Ensure the process has terminated + process.wait() diff --git a/tests/e2e_tests/multistep/__init__.py b/tests/e2e_tests/multistep/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/e2e_tests/multistep/test_last_tx_block.py b/tests/e2e_tests/multistep/test_last_tx_block.py new file mode 100644 index 0000000000..0d1796f5d8 --- /dev/null +++ b/tests/e2e_tests/multistep/test_last_tx_block.py @@ -0,0 +1,51 @@ +from bittensor.commands.root import RootRegisterCommand +from bittensor.commands.delegates import NominateCommand +from bittensor.commands.network import RegisterSubnetworkCommand +from bittensor.commands.register import RegisterCommand +from ..utils import setup_wallet + + +# Automated testing for take related tests described in +# https://discord.com/channels/799672011265015819/1176889736636407808/1236057424134144152 +def test_takes(local_chain): + # Register root as Alice + (keypair, exec_command) = setup_wallet("//Alice") + exec_command(RootRegisterCommand, ["root", "register"]) + + # Create subnet 1 and verify created successfully + assert not (local_chain.query("SubtensorModule", "NetworksAdded", [1]).serialize()) + + exec_command(RegisterSubnetworkCommand, ["s", "create"]) + assert local_chain.query("SubtensorModule", "NetworksAdded", [1]) + + assert local_chain.query("SubtensorModule", "NetworksAdded", [1]).serialize() + + # Register and nominate Bob + (keypair, exec_command) = setup_wallet("//Bob") + assert ( + local_chain.query( + "SubtensorModule", "LastTxBlock", [keypair.ss58_address] + ).serialize() + == 0 + ) + + assert ( + local_chain.query( + "SubtensorModule", "LastTxBlockDelegateTake", [keypair.ss58_address] + ).serialize() + == 0 + ) + exec_command(RegisterCommand, ["s", "register", "--neduid", "1"]) + exec_command(NominateCommand, ["root", "nominate"]) + assert ( + local_chain.query( + "SubtensorModule", "LastTxBlock", [keypair.ss58_address] + ).serialize() + > 0 + ) + assert ( + local_chain.query( + "SubtensorModule", "LastTxBlockDelegateTake", [keypair.ss58_address] + ).serialize() + > 0 + ) diff --git a/tests/e2e_tests/subcommands/__init__.py b/tests/e2e_tests/subcommands/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/e2e_tests/subcommands/delegation/__init__.py b/tests/e2e_tests/subcommands/delegation/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/e2e_tests/subcommands/delegation/test_set_delegate_take.py b/tests/e2e_tests/subcommands/delegation/test_set_delegate_take.py new file mode 100644 index 0000000000..0453576332 --- /dev/null +++ b/tests/e2e_tests/subcommands/delegation/test_set_delegate_take.py @@ -0,0 +1,56 @@ +from bittensor.commands.delegates import SetTakeCommand, NominateCommand +from bittensor.commands.network import RegisterSubnetworkCommand +from bittensor.commands.register import RegisterCommand +from bittensor.commands.root import RootRegisterCommand + +from tests.e2e_tests.utils import setup_wallet + + +def test_set_delegate_increase_take(local_chain): + # Register root as Alice + (keypair, exec_command) = setup_wallet("//Alice") + exec_command(RootRegisterCommand, ["root", "register"]) + + # Create subnet 1 and verify created successfully + assert not (local_chain.query("SubtensorModule", "NetworksAdded", [1]).serialize()) + + exec_command(RegisterSubnetworkCommand, ["s", "create"]) + assert local_chain.query("SubtensorModule", "NetworksAdded", [1]) + + assert local_chain.query("SubtensorModule", "NetworksAdded", [1]).serialize() + + # Register and nominate Bob + (keypair, exec_command) = setup_wallet("//Bob") + assert ( + local_chain.query( + "SubtensorModule", "LastTxBlock", [keypair.ss58_address] + ).serialize() + == 0 + ) + + assert ( + local_chain.query( + "SubtensorModule", "LastTxBlockDelegateTake", [keypair.ss58_address] + ).serialize() + == 0 + ) + exec_command(RegisterCommand, ["s", "register", "--netuid", "1"]) + exec_command(NominateCommand, ["root", "nominate"]) + assert ( + local_chain.query( + "SubtensorModule", "LastTxBlock", [keypair.ss58_address] + ).serialize() + > 0 + ) + assert ( + local_chain.query( + "SubtensorModule", "LastTxBlockDelegateTake", [keypair.ss58_address] + ).serialize() + > 0 + ) + + # Set delegate take for Bob + exec_command(SetTakeCommand, ["r", "set_take", "--take", "0.15"]) + assert local_chain.query( + "SubtensorModule", "Delegates", [keypair.ss58_address] + ).value == int(0.15 * 65535) diff --git a/tests/e2e_tests/subcommands/wallet/__init__.py b/tests/e2e_tests/subcommands/wallet/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/e2e_tests/subcommands/wallet/test_transfer.py b/tests/e2e_tests/subcommands/wallet/test_transfer.py new file mode 100644 index 0000000000..de8052e027 --- /dev/null +++ b/tests/e2e_tests/subcommands/wallet/test_transfer.py @@ -0,0 +1,32 @@ +from bittensor.commands.transfer import TransferCommand +from ...utils import setup_wallet +import bittensor + + +# Example test using the local_chain fixture +def test_transfer(local_chain): + (keypair, exec_command) = setup_wallet("//Alice") + + acc_before = local_chain.query("System", "Account", [keypair.ss58_address]) + exec_command( + TransferCommand, + [ + "wallet", + "transfer", + "--amount", + "2", + "--dest", + "5GpzQgpiAKHMWNSH3RN4GLf96GVTDct9QxYEFAY7LWcVzTbx", + ], + ) + acc_after = local_chain.query("System", "Account", [keypair.ss58_address]) + + expected_transfer = 2_000_000_000 + tolerance = 200_000 # Tx fee tolerance + + actual_difference = ( + acc_before.value["data"]["free"] - acc_after.value["data"]["free"] + ) + assert ( + expected_transfer <= actual_difference <= expected_transfer + tolerance + ), f"Expected transfer with tolerance: {expected_transfer} <= {actual_difference} <= {expected_transfer + tolerance}" diff --git a/tests/e2e_tests/utils.py b/tests/e2e_tests/utils.py new file mode 100644 index 0000000000..3ad789dd6d --- /dev/null +++ b/tests/e2e_tests/utils.py @@ -0,0 +1,32 @@ +from substrateinterface import Keypair +from typing import List +import bittensor + + +def setup_wallet(uri: str): + keypair = Keypair.create_from_uri(uri) + wallet_path = "/tmp/btcli-e2e-wallet-{}".format(uri.strip("/")) + wallet = bittensor.wallet(path=wallet_path) + wallet.set_coldkey(keypair=keypair, encrypt=False, overwrite=True) + wallet.set_coldkeypub(keypair=keypair, encrypt=False, overwrite=True) + wallet.set_hotkey(keypair=keypair, encrypt=False, overwrite=True) + + def exec_command(command, extra_args: List[str]): + parser = bittensor.cli.__create_parser__() + args = extra_args + [ + "--no_prompt", + "--subtensor.network", + "local", + "--subtensor.chain_endpoint", + "ws://localhost:9945", + "--wallet.path", + wallet_path, + ] + config = bittensor.config( + parser=parser, + args=args, + ) + cli_instance = bittensor.cli(config) + command.run(cli_instance) + + return (keypair, exec_command) From 3e2ec711b186f2445953f7f9bf4f5b285827385d Mon Sep 17 00:00:00 2001 From: Gus Date: Mon, 20 May 2024 13:42:16 -0400 Subject: [PATCH 2/9] chore: bump version --- VERSION | 2 +- bittensor/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index ff61e18689..cf1279ccf0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.12.1 +6.12.2 \ No newline at end of file diff --git a/bittensor/__init__.py b/bittensor/__init__.py index 3f5c1d99ec..8678197e0b 100644 --- a/bittensor/__init__.py +++ b/bittensor/__init__.py @@ -28,7 +28,7 @@ # Bittensor code and protocol version. -__version__ = "6.12.1" +__version__ = "6.12.2" version_split = __version__.split(".") __version_as_int__: int = ( From d1d765390a627559f1026acd76a652b36bc1001b Mon Sep 17 00:00:00 2001 From: Gus Date: Mon, 20 May 2024 13:47:55 -0400 Subject: [PATCH 3/9] chore: update changelog --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f850bb076..65f791017d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 6.12.2 / 2024-05-20 + +## What's Changed +* Add setting delegate take + + +**Full Changelog**: https://github.com/opentensor/bittensor/compare/v6.12.1...b4919ec9d9149f6add7e1ef57e4e4367f86224c5 + + ## 6.12.1 / 2024-05-17 ## What's Changed From 9ea370d802f0a971846b9bd875da6054383053ef Mon Sep 17 00:00:00 2001 From: Gus Date: Mon, 20 May 2024 13:51:49 -0400 Subject: [PATCH 4/9] fmt --- bittensor/commands/delegates.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bittensor/commands/delegates.py b/bittensor/commands/delegates.py index 25ea195d5e..5d7ff4fcd8 100644 --- a/bittensor/commands/delegates.py +++ b/bittensor/commands/delegates.py @@ -101,9 +101,9 @@ def show_delegates( for prev_delegate in prev_delegates: prev_delegates_dict[prev_delegate.hotkey_ss58] = prev_delegate - registered_delegate_info: Optional[Dict[str, DelegatesDetails]] = ( - get_delegates_details(url=bittensor.__delegates_details_url__) - ) + registered_delegate_info: Optional[ + Dict[str, DelegatesDetails] + ] = get_delegates_details(url=bittensor.__delegates_details_url__) if registered_delegate_info is None: bittensor.__console__.print( ":warning:[yellow]Could not get delegate info from chain.[/yellow]" @@ -807,9 +807,9 @@ def _run(cli: "bittensor.cli", subtensor: "bittensor.subtensor"): delegates.sort(key=lambda delegate: delegate[0].total_stake, reverse=True) total_delegated += sum(my_delegates.values()) - registered_delegate_info: Optional[DelegatesDetails] = ( - get_delegates_details(url=bittensor.__delegates_details_url__) - ) + registered_delegate_info: Optional[ + DelegatesDetails + ] = get_delegates_details(url=bittensor.__delegates_details_url__) if registered_delegate_info is None: bittensor.__console__.print( ":warning:[yellow]Could not get delegate info from chain.[/yellow]" From 54eee604c00ac4f04a31d5d7bc663124731a34d8 Mon Sep 17 00:00:00 2001 From: Ibraheem Nadeem Date: Thu, 2 May 2024 19:41:33 -0700 Subject: [PATCH 5/9] Improve: Introduced _do_set_root_weights Check for minimum staking when unstaking PR --- bittensor/commands/unstake.py | 31 +++--- bittensor/extrinsics/root.py | 4 +- bittensor/extrinsics/unstaking.py | 36 +++++++ bittensor/mock/subtensor_mock.py | 12 +++ bittensor/subtensor.py | 79 ++++++++++++++++ tests/integration_tests/test_cli.py | 141 ++++++++++++++++++++++++++++ 6 files changed, 287 insertions(+), 16 deletions(-) diff --git a/bittensor/commands/unstake.py b/bittensor/commands/unstake.py index a2290d5b84..87d13aab91 100644 --- a/bittensor/commands/unstake.py +++ b/bittensor/commands/unstake.py @@ -85,22 +85,23 @@ def check_config(cls, config: "bittensor.config"): hotkeys = str(config.hotkeys).replace("[", "").replace("]", "") else: hotkeys = str(config.wallet.hotkey) - if not Confirm.ask( - "Unstake all Tao from: [bold]'{}'[/bold]?".format(hotkeys) - ): - amount = Prompt.ask("Enter Tao amount to unstake") - config.unstake_all = False - try: - config.amount = float(amount) - except ValueError: - console.print( - ":cross_mark:[red] Invalid Tao amount[/red] [bold white]{}[/bold white]".format( - amount - ) - ) - sys.exit() - else: + if config.no_prompt: config.unstake_all = True + else: + # I really don't like this logic flow. It can be a bit confusing to read for something + # as serious as unstaking all. + if Confirm.ask(f"Unstake all Tao from: [bold]'{hotkeys}'[/bold]?"): + config.unstake_all = True + else: + config.unstake_all = False + amount = Prompt.ask("Enter Tao amount to unstake") + try: + config.amount = float(amount) + except ValueError: + console.print( + f":cross_mark:[red] Invalid Tao amount[/red] [bold white]{amount}[/bold white]" + ) + sys.exit() @staticmethod def add_args(command_parser): diff --git a/bittensor/extrinsics/root.py b/bittensor/extrinsics/root.py index ab8b314870..e500e9554f 100644 --- a/bittensor/extrinsics/root.py +++ b/bittensor/extrinsics/root.py @@ -129,6 +129,8 @@ def set_root_weights_extrinsic( 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``. """ + wallet.coldkey # unlock coldkey + # First convert types. if isinstance(netuids, list): netuids = torch.tensor(netuids, dtype=torch.int64) @@ -176,7 +178,7 @@ def set_root_weights_extrinsic( weight_uids, weight_vals = weight_utils.convert_weights_and_uids_for_emit( netuids, weights ) - success, error_message = subtensor._do_set_weights( + success, error_message = subtensor._do_set_root_weights( wallet=wallet, netuid=0, uids=weight_uids, diff --git a/bittensor/extrinsics/unstaking.py b/bittensor/extrinsics/unstaking.py index 5fca6b119b..6046124f40 100644 --- a/bittensor/extrinsics/unstaking.py +++ b/bittensor/extrinsics/unstaking.py @@ -71,6 +71,32 @@ def __do_remove_stake_single( return success +def check_threshold_amount( + subtensor: "bittensor.subtensor", unstaking_balance: Balance +) -> bool: + """ + Checks if the unstaking amount is above the threshold or 0 + + Args: + unstaking_balance (Balance): + the balance to check for threshold limits. + + Returns: + success (bool): + ``true`` if the unstaking is above the threshold or 0, or ``false`` if the + unstaking is below the threshold, but not 0. + """ + min_req_stake: Balance = subtensor.get_minimum_required_stake() + + if min_req_stake > unstaking_balance > 0: + bittensor.__console__.print( + f":cross_mark: [red]Unstaking balance of {unstaking_balance} less than minimum of {min_req_stake} TAO[/red]" + ) + return False + else: + return True + + def unstake_extrinsic( subtensor: "bittensor.subtensor", wallet: "bittensor.wallet", @@ -134,6 +160,11 @@ def unstake_extrinsic( ) return False + if not check_threshold_amount( + subtensor=subtensor, unstaking_balance=unstaking_balance + ): + return False + # Ask before moving on. if prompt: if not Confirm.ask( @@ -305,6 +336,11 @@ def unstake_multiple_extrinsic( ) continue + if not check_threshold_amount( + subtensor=subtensor, unstaking_balance=unstaking_balance + ): + return False + # Ask before moving on. if prompt: if not Confirm.ask( diff --git a/bittensor/mock/subtensor_mock.py b/bittensor/mock/subtensor_mock.py index 7b944ec3fe..4ca08cfb22 100644 --- a/bittensor/mock/subtensor_mock.py +++ b/bittensor/mock/subtensor_mock.py @@ -1305,6 +1305,18 @@ def _do_unstake( return True + @staticmethod + def min_required_stake(): + """ + As the minimum required stake may change, this method allows us to dynamically + update the amount in the mock without updating the tests + """ + # valid minimum threshold as of 2024/05/01 + return 100_000_000 # RAO + + def get_minimum_required_stake(self): + return Balance.from_rao(self.min_required_stake()) + def get_delegate_by_hotkey( self, hotkey_ss58: str, block: Optional[int] = None ) -> Optional["DelegateInfo"]: diff --git a/bittensor/subtensor.py b/bittensor/subtensor.py index 3de3c0d3c9..d0581a3fd9 100644 --- a/bittensor/subtensor.py +++ b/bittensor/subtensor.py @@ -2230,6 +2230,73 @@ def root_set_weights( prompt=prompt, ) + def _do_set_root_weights( + self, + wallet: "bittensor.wallet", + uids: List[int], + vals: List[int], + netuid: int = 0, + version_key: int = bittensor.__version_as_int__, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = False, + ) -> Tuple[bool, Optional[str]]: # (success, error_message) + """ + Internal method to send a transaction to the Bittensor blockchain, setting weights + for specified neurons on root. This method constructs and submits the transaction, handling + retries and blockchain communication. + + Args: + wallet (bittensor.wallet): The wallet associated with the neuron setting the weights. + uids (List[int]): List of neuron UIDs for which weights are being set. + vals (List[int]): List of weight values corresponding to each UID. + netuid (int): Unique identifier for the network. + version_key (int, optional): Version key for compatibility with the network. + wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. + wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. + + Returns: + Tuple[bool, Optional[str]]: A tuple containing a success flag and an optional error message. + + This method is vital for the dynamic weighting mechanism in Bittensor, where neurons adjust their + trust in other neurons based on observed performance and contributions on the root network. + """ + + @retry(delay=2, tries=3, backoff=2, max_delay=4, logger=logger) + def make_substrate_call_with_retry(): + call = self.substrate.compose_call( + call_module="SubtensorModule", + call_function="set_root_weights", + call_params={ + "dests": uids, + "weights": vals, + "netuid": netuid, + "version_key": version_key, + "hotkey": wallet.hotkey.ss58_address, + }, + ) + # Period dictates how long the extrinsic will stay as part of waiting pool + extrinsic = self.substrate.create_signed_extrinsic( + call=call, + keypair=wallet.coldkey, + era={"period": 5}, + ) + response = self.substrate.submit_extrinsic( + extrinsic, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + # We only wait here if we expect finalization. + if not wait_for_finalization and not wait_for_inclusion: + return True, "Not waiting for finalziation or inclusion." + + response.process_events() + if response.is_success: + return True, "Successfully set weights." + else: + return False, response.error_message + + return make_substrate_call_with_retry() + ######################## #### Registry Calls #### ######################## @@ -3723,6 +3790,18 @@ def get_stake_info_for_coldkeys( return StakeInfo.list_of_tuple_from_vec_u8(bytes_result) # type: ignore + def get_minimum_required_stake( + self, + ): + @retry(delay=2, tries=3, backoff=2, max_delay=4, logger=logger) + def make_substrate_call_with_retry(): + return self.substrate.query( + module="SubtensorModule", storage_function="NominatorMinRequiredStake" + ) + + result = make_substrate_call_with_retry() + return Balance.from_rao(result.decode()) + ######################################## #### Neuron information per subnet #### ######################################## diff --git a/tests/integration_tests/test_cli.py b/tests/integration_tests/test_cli.py index a0e9f425cf..5c6c9f377a 100644 --- a/tests/integration_tests/test_cli.py +++ b/tests/integration_tests/test_cli.py @@ -770,6 +770,147 @@ def mock_get_wallet(*args, **kwargs): stake.tao, mock_stakes[wallet.hotkey_str].tao, places=4 ) + def test_unstake_with_thresholds(self, _): + config = self.config + config.command = "stake" + config.subcommand = "remove" + config.no_prompt = True + # as the minimum required stake may change, this method allows us to dynamically + # update the amount in the mock without updating the tests + config.amount = Balance.from_rao(_subtensor_mock.min_required_stake() - 1) + config.wallet.name = "fake_wallet" + config.hotkeys = ["hk0", "hk1", "hk2"] + config.all_hotkeys = False + # Notice no max_stake specified + + mock_stakes: Dict[str, Balance] = { + "hk0": Balance.from_float(10.0), + "hk1": Balance.from_float(11.1), + "hk2": Balance.from_float(12.2), + } + + mock_coldkey_kp = _get_mock_keypair(0, self.id()) + + mock_wallets = [ + SimpleNamespace( + name=config.wallet.name, + coldkey=mock_coldkey_kp, + coldkeypub=mock_coldkey_kp, + hotkey_str=hk, + hotkey=_get_mock_keypair(idx + 100, self.id()), + ) + for idx, hk in enumerate(config.hotkeys) + ] + + # Register mock wallets and give them stakes + + for wallet in mock_wallets: + _ = _subtensor_mock.force_register_neuron( + netuid=1, + hotkey=wallet.hotkey.ss58_address, + coldkey=wallet.coldkey.ss58_address, + stake=mock_stakes[wallet.hotkey_str].rao, + ) + + cli = bittensor.cli(config) + + def mock_get_wallet(*args, **kwargs): + if kwargs.get("hotkey"): + for wallet in mock_wallets: + if wallet.hotkey_str == kwargs.get("hotkey"): + return wallet + else: + return mock_wallets[0] + + with patch("bittensor.wallet") as mock_create_wallet: + mock_create_wallet.side_effect = mock_get_wallet + + # Check stakes before unstaking + for wallet in mock_wallets: + stake = _subtensor_mock.get_stake_for_coldkey_and_hotkey( + hotkey_ss58=wallet.hotkey.ss58_address, + coldkey_ss58=wallet.coldkey.ss58_address, + ) + self.assertEqual(stake.rao, mock_stakes[wallet.hotkey_str].rao) + + cli.run() + + # Check stakes after unstaking + for wallet in mock_wallets: + stake = _subtensor_mock.get_stake_for_coldkey_and_hotkey( + hotkey_ss58=wallet.hotkey.ss58_address, + coldkey_ss58=wallet.coldkey.ss58_address, + ) + # because the amount is less than the threshold, none of these should unstake + self.assertEqual(stake.tao, mock_stakes[wallet.hotkey_str].tao) + + def test_unstake_all(self, _): + config = self.config + config.command = "stake" + config.subcommand = "remove" + config.no_prompt = True + config.amount = 0.0 # 0 implies full unstake + config.wallet.name = "fake_wallet" + config.hotkeys = ["hk0"] + config.all_hotkeys = False + + mock_stakes: Dict[str, Balance] = {"hk0": Balance.from_float(10.0)} + + mock_coldkey_kp = _get_mock_keypair(0, self.id()) + + mock_wallets = [ + SimpleNamespace( + name=config.wallet.name, + coldkey=mock_coldkey_kp, + coldkeypub=mock_coldkey_kp, + hotkey_str=hk, + hotkey=_get_mock_keypair(idx + 100, self.id()), + ) + for idx, hk in enumerate(config.hotkeys) + ] + + # Register mock wallets and give them stakes + + for wallet in mock_wallets: + _ = _subtensor_mock.force_register_neuron( + netuid=1, + hotkey=wallet.hotkey.ss58_address, + coldkey=wallet.coldkey.ss58_address, + stake=mock_stakes[wallet.hotkey_str].rao, + ) + + cli = bittensor.cli(config) + + def mock_get_wallet(*args, **kwargs): + if kwargs.get("hotkey"): + for wallet in mock_wallets: + if wallet.hotkey_str == kwargs.get("hotkey"): + return wallet + else: + return mock_wallets[0] + + with patch("bittensor.wallet") as mock_create_wallet: + mock_create_wallet.side_effect = mock_get_wallet + + # Check stakes before unstaking + for wallet in mock_wallets: + stake = _subtensor_mock.get_stake_for_coldkey_and_hotkey( + hotkey_ss58=wallet.hotkey.ss58_address, + coldkey_ss58=wallet.coldkey.ss58_address, + ) + self.assertEqual(stake.rao, mock_stakes[wallet.hotkey_str].rao) + + cli.run() + + # Check stakes after unstaking + for wallet in mock_wallets: + stake = _subtensor_mock.get_stake_for_coldkey_and_hotkey( + hotkey_ss58=wallet.hotkey.ss58_address, + coldkey_ss58=wallet.coldkey.ss58_address, + ) + # because the amount is less than the threshold, none of these should unstake + self.assertEqual(stake.tao, Balance.from_tao(0)) + def test_stake_with_specific_hotkeys(self, _): config = self.config config.command = "stake" From 17904105e39d2d3dc2818811f3f903189496cf80 Mon Sep 17 00:00:00 2001 From: gus-opentensor <158077861+gus-opentensor@users.noreply.github.com> Date: Mon, 20 May 2024 15:34:01 -0400 Subject: [PATCH 6/9] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65f791017d..f71f4e0011 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ * Add setting delegate take -**Full Changelog**: https://github.com/opentensor/bittensor/compare/v6.12.1...b4919ec9d9149f6add7e1ef57e4e4367f86224c5 +**Full Changelog**: https://github.com/opentensor/bittensor/compare/v6.12.1...54eee604c00ac4f04a31d5d7bc663124731a34d8 ## 6.12.1 / 2024-05-17 From ebcc8a37a9b4b334b9494bcf9060cc92121158a1 Mon Sep 17 00:00:00 2001 From: Gus Date: Wed, 22 May 2024 12:06:40 -0400 Subject: [PATCH 7/9] chore: rm _do_set_root_weights --- bittensor/extrinsics/root.py | 3 +- bittensor/subtensor.py | 67 ------------------------------------ 2 files changed, 1 insertion(+), 69 deletions(-) diff --git a/bittensor/extrinsics/root.py b/bittensor/extrinsics/root.py index e500e9554f..5c98eebe8b 100644 --- a/bittensor/extrinsics/root.py +++ b/bittensor/extrinsics/root.py @@ -129,7 +129,6 @@ def set_root_weights_extrinsic( 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``. """ - wallet.coldkey # unlock coldkey # First convert types. if isinstance(netuids, list): @@ -178,7 +177,7 @@ def set_root_weights_extrinsic( weight_uids, weight_vals = weight_utils.convert_weights_and_uids_for_emit( netuids, weights ) - success, error_message = subtensor._do_set_root_weights( + success, error_message = subtensor._do_set_weights( wallet=wallet, netuid=0, uids=weight_uids, diff --git a/bittensor/subtensor.py b/bittensor/subtensor.py index d0581a3fd9..e7add3abdf 100644 --- a/bittensor/subtensor.py +++ b/bittensor/subtensor.py @@ -2230,73 +2230,6 @@ def root_set_weights( prompt=prompt, ) - def _do_set_root_weights( - self, - wallet: "bittensor.wallet", - uids: List[int], - vals: List[int], - netuid: int = 0, - version_key: int = bittensor.__version_as_int__, - wait_for_inclusion: bool = False, - wait_for_finalization: bool = False, - ) -> Tuple[bool, Optional[str]]: # (success, error_message) - """ - Internal method to send a transaction to the Bittensor blockchain, setting weights - for specified neurons on root. This method constructs and submits the transaction, handling - retries and blockchain communication. - - Args: - wallet (bittensor.wallet): The wallet associated with the neuron setting the weights. - uids (List[int]): List of neuron UIDs for which weights are being set. - vals (List[int]): List of weight values corresponding to each UID. - netuid (int): Unique identifier for the network. - version_key (int, optional): Version key for compatibility with the network. - wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. - wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. - - Returns: - Tuple[bool, Optional[str]]: A tuple containing a success flag and an optional error message. - - This method is vital for the dynamic weighting mechanism in Bittensor, where neurons adjust their - trust in other neurons based on observed performance and contributions on the root network. - """ - - @retry(delay=2, tries=3, backoff=2, max_delay=4, logger=logger) - def make_substrate_call_with_retry(): - call = self.substrate.compose_call( - call_module="SubtensorModule", - call_function="set_root_weights", - call_params={ - "dests": uids, - "weights": vals, - "netuid": netuid, - "version_key": version_key, - "hotkey": wallet.hotkey.ss58_address, - }, - ) - # Period dictates how long the extrinsic will stay as part of waiting pool - extrinsic = self.substrate.create_signed_extrinsic( - call=call, - keypair=wallet.coldkey, - era={"period": 5}, - ) - response = self.substrate.submit_extrinsic( - extrinsic, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) - # We only wait here if we expect finalization. - if not wait_for_finalization and not wait_for_inclusion: - return True, "Not waiting for finalziation or inclusion." - - response.process_events() - if response.is_success: - return True, "Successfully set weights." - else: - return False, response.error_message - - return make_substrate_call_with_retry() - ######################## #### Registry Calls #### ######################## From bea05d687d9dc0af07ea232afa6d9e3180e9d602 Mon Sep 17 00:00:00 2001 From: Liam Date: Wed, 15 May 2024 21:06:55 +0400 Subject: [PATCH 8/9] fix: deprecated transfer method usage --- bittensor/subtensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bittensor/subtensor.py b/bittensor/subtensor.py index e7add3abdf..26c1c52bbd 100644 --- a/bittensor/subtensor.py +++ b/bittensor/subtensor.py @@ -1200,7 +1200,7 @@ def get_transfer_fee( call = self.substrate.compose_call( call_module="Balances", - call_function="transfer", + call_function="transfer_allow_death", call_params={"dest": dest, "value": transfer_balance.rao}, ) @@ -1245,7 +1245,7 @@ def _do_transfer( def make_substrate_call_with_retry(): call = self.substrate.compose_call( call_module="Balances", - call_function="transfer", + call_function="transfer_allow_death", call_params={"dest": dest, "value": transfer_balance.rao}, ) extrinsic = self.substrate.create_signed_extrinsic( From 6f5bb7320d546d90e1e1ae0174d1d7432e35937a Mon Sep 17 00:00:00 2001 From: gus-opentensor <158077861+gus-opentensor@users.noreply.github.com> Date: Wed, 22 May 2024 12:44:12 -0400 Subject: [PATCH 9/9] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f71f4e0011..9123de2c44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ## What's Changed * Add setting delegate take - +* fix: deprecated transfer method usage **Full Changelog**: https://github.com/opentensor/bittensor/compare/v6.12.1...54eee604c00ac4f04a31d5d7bc663124731a34d8