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
18 changes: 16 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
# Changelog

## 8.3.0 /2024-11-06
## 8.3.1 /2024-11-13

## What's Changed
* Better handle incorrect file path for wallets. by @thewhaleking in https://github.com/opentensor/btcli/pull/230
* Handle websockets version 14, verbose error output by @thewhaleking in https://github.com/opentensor/btcli/pull/236
* Handles the new PasswordError from bt-wallet by @thewhaleking in https://github.com/opentensor/btcli/pull/232

**Full Changelog**: https://github.com/opentensor/btcli/compare/v8.3.0...v.8.3.1

## 8.3.0 /2024-11-06

## What's Changed
* Better handle incorrect password by @thewhaleking in https://github.com/opentensor/btcli/pull/187
* Fixes success path of pow register by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/189
* Adds `--all` flag to transfer by @thewhaleking in https://github.com/opentensor/btcli/pull/181
* Various fixes by @thewhaleking in https://github.com/opentensor/btcli/pull/199
* In `do_transfer`, we check the balance with coldkeypub.ss58, but then retrieve it from the dict with coldkey.ss58. Resolve this. by @thewhaleking in https://github.com/opentensor/btcli/pull/199
* Handle KeyboardInterrupt in CLI to gracefully exit (no traceback) by @thewhaleking in https://github.com/opentensor/btcli/pull/199
* Handle race conditions where self.metadata may not be set before finishing initialising runtime (this may need optimised in the future) by @thewhaleking in https://github.com/opentensor/btcli/pull/199
* Error description output by @thewhaleking in https://github.com/opentensor/btcli/pull/199
* Taostats link fixed by @thewhaleking in https://github.com/opentensor/btcli/pull/199
* Fixes not showing confirmation if --no-prompt is specified on stake remove by @thewhaleking in https://github.com/opentensor/btcli/pull/199
* Fix wallets in overview by @thewhaleking in https://github.com/opentensor/btcli/pull/197
* fix handling null neurons by @thewhaleking in https://github.com/opentensor/btcli/pull/214
* Fix cuda pow registration by @thewhaleking in https://github.com/opentensor/btcli/pull/215
Expand All @@ -16,6 +29,7 @@
* Support hotkey names for include/exclude in st add/remove by @thewhaleking in https://github.com/opentensor/btcli/pull/216
* Subvortex network added by @thewhaleking in https://github.com/opentensor/btcli/pull/223
* Add prompt option to all commands which use Confirm prompts by @thewhaleking in https://github.com/opentensor/btcli/pull/227
* fix: local subtensor port by @distributedstatemachine in https://github.com/opentensor/btcli/pull/228
* Update local subtensor port by @distributedstatemachine in https://github.com/opentensor/btcli/pull/228

**Full Changelog**: https://github.com/opentensor/btcli/compare/v8.2.0...v8.3.0
Expand Down
2 changes: 1 addition & 1 deletion bittensor_cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
from .cli import CLIManager


__version__ = "8.3.0"
__version__ = "8.3.1"

__all__ = ["CLIManager", "__version__"]
8 changes: 7 additions & 1 deletion bittensor_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import re
import ssl
import sys
import traceback
from pathlib import Path
from typing import Coroutine, Optional
from dataclasses import fields
Expand Down Expand Up @@ -57,7 +58,7 @@ class GitError(Exception):
pass


__version__ = "8.3.0"
__version__ = "8.3.1"


_core_version = re.match(r"^\d+\.\d+\.\d+", __version__).group(0)
Expand Down Expand Up @@ -840,13 +841,18 @@ async def _run():
return result
except (ConnectionRefusedError, ssl.SSLError):
err_console.print(f"Unable to connect to the chain: {self.subtensor}")
verbose_console.print(traceback.format_exc())
except (
ConnectionClosed,
SubstrateRequestException,
KeyboardInterrupt,
) as e:
if isinstance(e, SubstrateRequestException):
err_console.print(str(e))
verbose_console.print(traceback.format_exc())
except Exception as e:
err_console.print(f"An unknown error has occurred: {e}")
verbose_console.print(traceback.format_exc())
finally:
if initiated is False:
asyncio.create_task(cmd).cancel()
Expand Down
20 changes: 10 additions & 10 deletions bittensor_cli/src/bittensor/async_substrate_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@
from hashlib import blake2b
from typing import Optional, Any, Union, Callable, Awaitable, cast

from bt_decode import PortableRegistry, decode as decode_by_type_string, MetadataV15
from async_property import async_property
from bt_decode import PortableRegistry, decode as decode_by_type_string, MetadataV15
from bittensor_wallet import Keypair
from packaging import version
from scalecodec import GenericExtrinsic
from scalecodec.base import ScaleBytes, ScaleType, RuntimeConfigurationObject
from scalecodec.type_registry import load_type_registry_preset
from scalecodec.types import GenericCall
from bittensor_wallet import Keypair
from substrateinterface.exceptions import (
SubstrateRequestException,
ExtrinsicNotFound,
Expand Down Expand Up @@ -771,14 +772,13 @@ def __init__(
"""
self.chain_endpoint = chain_endpoint
self.__chain = chain_name
self.ws = Websocket(
chain_endpoint,
options={
"max_size": 2**32,
"read_limit": 2**16,
"write_limit": 2**16,
},
)
options = {
"max_size": 2**32,
"write_limit": 2**16,
}
if version.parse(websockets.__version__) < version.parse("14.0"):
options.update({"read_limit": 2**16})
self.ws = Websocket(chain_endpoint, options=options)
self._lock = asyncio.Lock()
self.last_block_hash: Optional[str] = None
self.config = {
Expand Down
13 changes: 4 additions & 9 deletions bittensor_cli/src/bittensor/extrinsics/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

import backoff
from bittensor_wallet import Wallet
from bittensor_wallet.errors import KeyFileError
from Crypto.Hash import keccak
import numpy as np
from rich.prompt import Confirm
Expand All @@ -37,6 +36,7 @@
get_human_readable,
print_verbose,
print_error,
unlock_key,
)

if typing.TYPE_CHECKING:
Expand Down Expand Up @@ -726,10 +726,8 @@ async def run_faucet_extrinsic(
return False, "Requires torch"

# Unlock coldkey
try:
wallet.unlock_coldkey()
except KeyFileError:
return False, "There was an error unlocking your coldkey"
if not (unlock_status := unlock_key(wallet, print_out=False)).success:
return False, unlock_status.message

# Get previous balance.
old_balance = await subtensor.get_balance(wallet.coldkeypub.ss58_address)
Expand Down Expand Up @@ -1639,10 +1637,7 @@ async def swap_hotkey_extrinsic(
)
return False

try:
wallet.unlock_coldkey()
except KeyFileError:
err_console.print("Error decrypting coldkey (possibly incorrect password)")
if not unlock_key(wallet).success:
return False

if prompt:
Expand Down
12 changes: 3 additions & 9 deletions bittensor_cli/src/bittensor/extrinsics/root.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
from typing import Union, List, TYPE_CHECKING

from bittensor_wallet import Wallet, Keypair
from bittensor_wallet.errors import KeyFileError
import numpy as np
from numpy.typing import NDArray
from rich.prompt import Confirm
Expand All @@ -37,6 +36,7 @@
u16_normalized_float,
print_verbose,
format_error_message,
unlock_key,
)

if TYPE_CHECKING:
Expand Down Expand Up @@ -306,10 +306,7 @@ async def root_register_extrinsic(
the response is `True`.
"""

try:
wallet.unlock_coldkey()
except KeyFileError:
err_console.print("Error decrypting coldkey (possibly incorrect password)")
if not unlock_key(wallet).success:
return False

print_verbose(f"Checking if hotkey ({wallet.hotkey_str}) is registered on root")
Expand Down Expand Up @@ -427,10 +424,7 @@ async def _do_set_weights():
err_console.print("Your hotkey is not registered to the root network")
return False

try:
wallet.unlock_coldkey()
except KeyFileError:
err_console.print("Error decrypting coldkey (possibly incorrect password)")
if not unlock_key(wallet).success:
return False

# First convert types.
Expand Down
7 changes: 2 additions & 5 deletions bittensor_cli/src/bittensor/extrinsics/transfer.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import asyncio

from bittensor_wallet import Wallet
from bittensor_wallet.errors import KeyFileError
from rich.prompt import Confirm
from substrateinterface.exceptions import SubstrateRequestException

Expand All @@ -16,6 +15,7 @@
get_explorer_url_for_network,
is_valid_bittensor_address_or_public_key,
print_error,
unlock_key,
)


Expand Down Expand Up @@ -115,10 +115,7 @@ async def do_transfer() -> tuple[bool, str, str]:
return False
console.print(f"[dark_orange]Initiating transfer on network: {subtensor.network}")
# Unlock wallet coldkey.
try:
wallet.unlock_coldkey()
except KeyFileError:
err_console.print("Error decrypting coldkey (possibly incorrect password)")
if not unlock_key(wallet).success:
return False

# Check balance.
Expand Down
54 changes: 48 additions & 6 deletions bittensor_cli/src/bittensor/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ast
from collections import namedtuple
import math
import os
import sqlite3
Expand All @@ -9,7 +10,7 @@

from bittensor_wallet import Wallet, Keypair
from bittensor_wallet.utils import SS58_FORMAT
from bittensor_wallet.errors import KeyFileError
from bittensor_wallet.errors import KeyFileError, PasswordError
from bittensor_wallet import utils
from jinja2 import Template
from markupsafe import Markup
Expand All @@ -35,6 +36,8 @@
err_console = Console(stderr=True)
verbose_console = Console(quiet=True)

UnlockStatus = namedtuple("UnlockStatus", ["success", "message"])


def print_console(message: str, colour: str, title: str, console: Console):
console.print(
Expand Down Expand Up @@ -238,11 +241,14 @@ def get_hotkey_wallets_for_wallet(
def get_coldkey_wallets_for_path(path: str) -> list[Wallet]:
"""Gets all wallets with coldkeys from a given path"""
wallet_path = Path(path).expanduser()
wallets = [
Wallet(name=directory.name, path=path)
for directory in wallet_path.iterdir()
if directory.is_dir()
]
try:
wallets = [
Wallet(name=directory.name, path=path)
for directory in wallet_path.iterdir()
if directory.is_dir()
]
except FileNotFoundError:
wallets = []
return wallets


Expand Down Expand Up @@ -974,3 +980,39 @@ def retry_prompt(
return var
else:
err_console.print(rejection_text)


def unlock_key(
wallet: Wallet, unlock_type="cold", print_out: bool = True
) -> "UnlockStatus":
"""
Attempts to decrypt a wallet's coldkey or hotkey
Args:
wallet: a Wallet object
unlock_type: the key type, 'cold' or 'hot'
print_out: whether to print out the error message to the err_console

Returns: UnlockStatus for success status of unlock, with error message if unsuccessful

"""
if unlock_type == "cold":
unlocker = "unlock_coldkey"
elif unlock_type == "hot":
unlocker = "unlock_hotkey"
else:
raise ValueError(
f"Invalid unlock type provided: {unlock_type}. Must be 'cold' or 'hot'."
)
try:
getattr(wallet, unlocker)()
return UnlockStatus(True, "")
except PasswordError:
err_msg = f"The password used to decrypt your {unlock_type.capitalize()}key Keyfile is invalid."
if print_out:
err_console.print(f":cross_mark: [red]{err_msg}[/red]")
return UnlockStatus(False, err_msg)
except KeyFileError:
err_msg = f"{unlock_type.capitalize()}key Keyfile is corrupt, non-writable, or non-readable, or non-existent."
if print_out:
err_console.print(f":cross_mark: [red]{err_msg}[/red]")
return UnlockStatus(False, err_msg)
30 changes: 6 additions & 24 deletions bittensor_cli/src/commands/root.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from typing import Optional, TYPE_CHECKING

from bittensor_wallet import Wallet
from bittensor_wallet.errors import KeyFileError
import numpy as np
from numpy.typing import NDArray
from rich import box
Expand Down Expand Up @@ -42,6 +41,7 @@
ss58_to_vec_u8,
update_metadata_table,
group_subnets,
unlock_key,
)

if TYPE_CHECKING:
Expand Down Expand Up @@ -280,10 +280,7 @@ async def burned_register_extrinsic(
finalization/inclusion, the response is `True`.
"""

try:
wallet.unlock_coldkey()
except KeyFileError:
err_console.print("Error decrypting coldkey (possibly incorrect password)")
if not unlock_key(wallet).success:
return False

with console.status(
Expand Down Expand Up @@ -537,10 +534,7 @@ async def get_stake_for_coldkey_and_hotkey(
delegate_string = "delegate" if delegate else "undelegate"

# Decrypt key
try:
wallet.unlock_coldkey()
except KeyFileError:
err_console.print("Error decrypting coldkey (possibly incorrect password)")
if not unlock_key(wallet).success:
return False

print_verbose("Checking if hotkey is a delegate")
Expand Down Expand Up @@ -1098,11 +1092,7 @@ async def senate_vote(
return False

# Unlock the wallet.
try:
wallet.unlock_hotkey()
wallet.unlock_coldkey()
except KeyFileError:
err_console.print("Error decrypting coldkey (possibly incorrect password)")
if not unlock_key(wallet).success and unlock_key(wallet, "hot").success:
return False

console.print(f"Fetching proposals in [dark_orange]network: {subtensor.network}")
Expand Down Expand Up @@ -1322,11 +1312,7 @@ async def _do_set_take() -> bool:

console.print(f"Setting take on [dark_orange]network: {subtensor.network}")
# Unlock the wallet.
try:
wallet.unlock_hotkey()
wallet.unlock_coldkey()
except KeyFileError:
err_console.print("Error decrypting coldkey (possibly incorrect password)")
if not unlock_key(wallet).success and unlock_key(wallet, "hot").success:
return False

result_ = await _do_set_take()
Expand Down Expand Up @@ -1724,11 +1710,7 @@ async def nominate(wallet: Wallet, subtensor: SubtensorInterface, prompt: bool):

console.print(f"Nominating on [dark_orange]network: {subtensor.network}")
# Unlock the wallet.
try:
wallet.unlock_hotkey()
wallet.unlock_coldkey()
except KeyFileError:
err_console.print("Error decrypting coldkey (possibly incorrect password)")
if not unlock_key(wallet).success and unlock_key(wallet, "hot").success:
return False

print_verbose(f"Checking hotkey ({wallet.hotkey_str}) is a delegate")
Expand Down
Loading