Browse Source

add some type hints

mostly related to hw wallets
hard-fail-on-bad-server-string
SomberNight 5 years ago
parent
commit
88307357ec
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 10
      electrum/base_wizard.py
  2. 17
      electrum/keystore.py
  3. 2
      electrum/plugin.py
  4. 3
      electrum/plugins/hw_wallet/plugin.py
  5. 33
      electrum/plugins/hw_wallet/qt.py

10
electrum/base_wizard.py

@ -34,7 +34,7 @@ from . import bitcoin
from . import keystore from . import keystore
from . import mnemonic from . import mnemonic
from .bip32 import is_bip32_derivation, xpub_type, normalize_bip32_derivation, BIP32Node from .bip32 import is_bip32_derivation, xpub_type, normalize_bip32_derivation, BIP32Node
from .keystore import bip44_derivation, purpose48_derivation from .keystore import bip44_derivation, purpose48_derivation, Hardware_KeyStore
from .wallet import (Imported_Wallet, Standard_Wallet, Multisig_Wallet, from .wallet import (Imported_Wallet, Standard_Wallet, Multisig_Wallet,
wallet_types, Wallet, Abstract_Wallet) wallet_types, Wallet, Abstract_Wallet)
from .storage import (WalletStorage, StorageEncryptionVersion, from .storage import (WalletStorage, StorageEncryptionVersion,
@ -47,7 +47,7 @@ from .logging import Logger
from .plugins.hw_wallet.plugin import OutdatedHwFirmwareException, HW_PluginBase from .plugins.hw_wallet.plugin import OutdatedHwFirmwareException, HW_PluginBase
if TYPE_CHECKING: if TYPE_CHECKING:
from .plugin import DeviceInfo from .plugin import DeviceInfo, BasePlugin
# hardware device setup purpose # hardware device setup purpose
@ -84,7 +84,7 @@ class BaseWizard(Logger):
self.data = {} self.data = {}
self.pw_args = None # type: Optional[WizardWalletPasswordSetting] self.pw_args = None # type: Optional[WizardWalletPasswordSetting]
self._stack = [] # type: List[WizardStackItem] self._stack = [] # type: List[WizardStackItem]
self.plugin = None self.plugin = None # type: Optional[BasePlugin]
self.keystores = [] self.keystores = []
self.is_kivy = config.get('gui') == 'kivy' self.is_kivy = config.get('gui') == 'kivy'
self.seed_type = None self.seed_type = None
@ -532,9 +532,9 @@ class BaseWizard(Logger):
encrypt_keystore = any(k.may_have_password() for k in self.keystores) encrypt_keystore = any(k.may_have_password() for k in self.keystores)
# note: the following condition ("if") is duplicated logic from # note: the following condition ("if") is duplicated logic from
# wallet.get_available_storage_encryption_version() # wallet.get_available_storage_encryption_version()
if self.wallet_type == 'standard' and isinstance(self.keystores[0], keystore.Hardware_KeyStore): if self.wallet_type == 'standard' and isinstance(self.keystores[0], Hardware_KeyStore):
# offer encrypting with a pw derived from the hw device # offer encrypting with a pw derived from the hw device
k = self.keystores[0] k = self.keystores[0] # type: Hardware_KeyStore
try: try:
k.handler = self.plugin.create_handler(self) k.handler = self.plugin.create_handler(self)
password = k.get_password_for_storage_encryption() password = k.get_password_for_storage_encryption()

17
electrum/keystore.py

@ -44,23 +44,25 @@ from .plugin import run_hook
from .logging import Logger from .logging import Logger
if TYPE_CHECKING: if TYPE_CHECKING:
from .gui.qt.util import TaskThread
from .transaction import Transaction, PartialTransaction, PartialTxInput, PartialTxOutput from .transaction import Transaction, PartialTransaction, PartialTxInput, PartialTxOutput
from .plugins.hw_wallet import HW_PluginBase, HardwareClientBase from .plugins.hw_wallet import HW_PluginBase, HardwareClientBase
class KeyStore(Logger): class KeyStore(Logger):
type: str
def __init__(self): def __init__(self):
Logger.__init__(self) Logger.__init__(self)
self.is_requesting_to_be_rewritten_to_wallet_file = False # type: bool self.is_requesting_to_be_rewritten_to_wallet_file = False # type: bool
def has_seed(self): def has_seed(self) -> bool:
return False return False
def is_watching_only(self): def is_watching_only(self) -> bool:
return False return False
def can_import(self): def can_import(self) -> bool:
return False return False
def get_type_text(self) -> str: def get_type_text(self) -> str:
@ -85,12 +87,12 @@ class KeyStore(Logger):
keypairs[pubkey.hex()] = derivation keypairs[pubkey.hex()] = derivation
return keypairs return keypairs
def can_sign(self, tx): def can_sign(self, tx) -> bool:
if self.is_watching_only(): if self.is_watching_only():
return False return False
return bool(self.get_tx_derivations(tx)) return bool(self.get_tx_derivations(tx))
def ready_to_sign(self): def ready_to_sign(self) -> bool:
return not self.is_watching_only() return not self.is_watching_only()
def dump(self) -> dict: def dump(self) -> dict:
@ -629,6 +631,7 @@ class Hardware_KeyStore(KeyStore, Xpub):
hw_type: str hw_type: str
device: str device: str
plugin: 'HW_PluginBase' plugin: 'HW_PluginBase'
thread: Optional['TaskThread'] = None
type = 'hardware' type = 'hardware'
@ -684,7 +687,7 @@ class Hardware_KeyStore(KeyStore, Xpub):
assert not self.has_seed() assert not self.has_seed()
return False return False
def get_password_for_storage_encryption(self): def get_password_for_storage_encryption(self) -> str:
from .storage import get_derivation_used_for_hw_device_encryption from .storage import get_derivation_used_for_hw_device_encryption
client = self.plugin.get_client(self) client = self.plugin.get_client(self)
derivation = get_derivation_used_for_hw_device_encryption() derivation = get_derivation_used_for_hw_device_encryption()
@ -692,7 +695,7 @@ class Hardware_KeyStore(KeyStore, Xpub):
password = self.get_pubkey_from_xpub(xpub, ()) password = self.get_pubkey_from_xpub(xpub, ())
return password return password
def has_usable_connection_with_device(self): def has_usable_connection_with_device(self) -> bool:
if not hasattr(self, 'plugin'): if not hasattr(self, 'plugin'):
return False return False
client = self.plugin.get_client(self, force_pair=False) client = self.plugin.get_client(self, force_pair=False)

2
electrum/plugin.py

@ -352,7 +352,7 @@ class DeviceMgr(ThreadJob):
ThreadJob.__init__(self) ThreadJob.__init__(self)
# Keyed by xpub. The value is the device id # Keyed by xpub. The value is the device id
# has been paired, and None otherwise. # has been paired, and None otherwise.
self.xpub_ids = {} self.xpub_ids = {} # type: Dict[str, str]
# A list of clients. The key is the client, the value is # A list of clients. The key is the client, the value is
# a (path, id_) pair. # a (path, id_) pair.
self.clients = {} # type: Dict[HardwareClientBase, Tuple[Union[str, bytes], str]] self.clients = {} # type: Dict[HardwareClientBase, Tuple[Union[str, bytes], str]]

3
electrum/plugins/hw_wallet/plugin.py

@ -40,6 +40,7 @@ if TYPE_CHECKING:
class HW_PluginBase(BasePlugin): class HW_PluginBase(BasePlugin):
keystore_class: Type['Hardware_KeyStore'] keystore_class: Type['Hardware_KeyStore']
libraries_available: bool
minimum_library = (0, ) minimum_library = (0, )
@ -211,7 +212,7 @@ def get_xpubs_and_der_suffixes_from_txinout(tx: PartialTransaction,
def only_hook_if_libraries_available(func): def only_hook_if_libraries_available(func):
# note: this decorator must wrap @hook, not the other way around, # note: this decorator must wrap @hook, not the other way around,
# as 'hook' uses the name of the function it wraps # as 'hook' uses the name of the function it wraps
def wrapper(self, *args, **kwargs): def wrapper(self: 'HW_PluginBase', *args, **kwargs):
if not self.libraries_available: return None if not self.libraries_available: return None
return func(self, *args, **kwargs) return func(self, *args, **kwargs)
return wrapper return wrapper

33
electrum/plugins/hw_wallet/qt.py

@ -26,6 +26,7 @@
import threading import threading
from functools import partial from functools import partial
from typing import TYPE_CHECKING, Union, Optional, Callable, Any
from PyQt5.QtCore import QObject, pyqtSignal from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtWidgets import QVBoxLayout, QLineEdit, QHBoxLayout, QLabel from PyQt5.QtWidgets import QVBoxLayout, QLineEdit, QHBoxLayout, QLabel
@ -33,12 +34,18 @@ from PyQt5.QtWidgets import QVBoxLayout, QLineEdit, QHBoxLayout, QLabel
from electrum.gui.qt.password_dialog import PasswordLayout, PW_PASSPHRASE from electrum.gui.qt.password_dialog import PasswordLayout, PW_PASSPHRASE
from electrum.gui.qt.util import (read_QIcon, WWLabel, OkButton, WindowModalDialog, from electrum.gui.qt.util import (read_QIcon, WWLabel, OkButton, WindowModalDialog,
Buttons, CancelButton, TaskThread, char_width_in_lineedit) Buttons, CancelButton, TaskThread, char_width_in_lineedit)
from electrum.gui.qt.main_window import StatusBarButton, ElectrumWindow
from electrum.i18n import _ from electrum.i18n import _
from electrum.logging import Logger from electrum.logging import Logger
from electrum.util import parse_URI, InvalidBitcoinURI from electrum.util import parse_URI, InvalidBitcoinURI, UserCancelled
from electrum.plugin import hook, DeviceUnpairableError
from .plugin import OutdatedHwFirmwareException from .plugin import OutdatedHwFirmwareException, HW_PluginBase
if TYPE_CHECKING:
from electrum.wallet import Abstract_Wallet
from electrum.keystore import Hardware_KeyStore
# The trickiest thing about this handler was getting windows properly # The trickiest thing about this handler was getting windows properly
@ -190,15 +197,10 @@ class QtHandlerBase(QObject, Logger):
self.done.set() self.done.set()
from electrum.plugin import hook
from electrum.util import UserCancelled
from electrum.gui.qt.main_window import StatusBarButton
class QtPluginBase(object): class QtPluginBase(object):
@hook @hook
def load_wallet(self, wallet, window): def load_wallet(self: Union['QtPluginBase', HW_PluginBase], wallet: 'Abstract_Wallet', window: ElectrumWindow):
for keystore in wallet.get_keystores(): for keystore in wallet.get_keystores():
if not isinstance(keystore, self.keystore_class): if not isinstance(keystore, self.keystore_class):
continue continue
@ -220,7 +222,8 @@ class QtPluginBase(object):
# Trigger a pairing # Trigger a pairing
keystore.thread.add(partial(self.get_client, keystore)) keystore.thread.add(partial(self.get_client, keystore))
def on_task_thread_error(self, window, keystore, exc_info): def on_task_thread_error(self: Union['QtPluginBase', HW_PluginBase], window: ElectrumWindow,
keystore: 'Hardware_KeyStore', exc_info):
e = exc_info[1] e = exc_info[1]
if isinstance(e, OutdatedHwFirmwareException): if isinstance(e, OutdatedHwFirmwareException):
if window.question(e.text_ignore_old_fw_and_continue(), title=_("Outdated device firmware")): if window.question(e.text_ignore_old_fw_and_continue(), title=_("Outdated device firmware")):
@ -236,7 +239,8 @@ class QtPluginBase(object):
else: else:
window.on_error(exc_info) window.on_error(exc_info)
def choose_device(self, window, keystore): def choose_device(self: Union['QtPluginBase', HW_PluginBase], window: ElectrumWindow,
keystore: 'Hardware_KeyStore') -> Optional[str]:
'''This dialog box should be usable even if the user has '''This dialog box should be usable even if the user has
forgotten their PIN or it is in bootloader mode.''' forgotten their PIN or it is in bootloader mode.'''
device_id = self.device_manager().xpub_id(keystore.xpub) device_id = self.device_manager().xpub_id(keystore.xpub)
@ -248,10 +252,12 @@ class QtPluginBase(object):
device_id = info.device.id_ device_id = info.device.id_
return device_id return device_id
def show_settings_dialog(self, window, keystore): def show_settings_dialog(self, window: ElectrumWindow, keystore: 'Hardware_KeyStore') -> None:
device_id = self.choose_device(window, keystore) device_id = self.choose_device(window, keystore)
def add_show_address_on_hw_device_button_for_receive_addr(self, wallet, keystore, main_window): def add_show_address_on_hw_device_button_for_receive_addr(self, wallet: 'Abstract_Wallet',
keystore: 'Hardware_KeyStore',
main_window: ElectrumWindow):
plugin = keystore.plugin plugin = keystore.plugin
receive_address_e = main_window.receive_address_e receive_address_e = main_window.receive_address_e
@ -267,3 +273,6 @@ class QtPluginBase(object):
keystore.thread.add(partial(plugin.show_address, wallet, addr, keystore)) keystore.thread.add(partial(plugin.show_address, wallet, addr, keystore))
dev_name = f"{plugin.device} ({keystore.label})" dev_name = f"{plugin.device} ({keystore.label})"
receive_address_e.addButton("eye1.png", show_address, _("Show on {}").format(dev_name)) receive_address_e.addButton("eye1.png", show_address, _("Show on {}").format(dev_name))
def create_handler(self, window: ElectrumWindow) -> 'QtHandlerBase':
raise NotImplementedError()

Loading…
Cancel
Save