diff --git a/electrum/wizard.py b/electrum/wizard.py index 1d4fdeaa3..8491f5e90 100644 --- a/electrum/wizard.py +++ b/electrum/wizard.py @@ -1,7 +1,7 @@ import copy import os -from typing import List, NamedTuple, Any, Dict, Optional, Tuple +from typing import List, NamedTuple, Any, Dict, Optional, Tuple, TYPE_CHECKING from electrum.i18n import _ from electrum.interface import ServerAddr @@ -16,6 +16,11 @@ from electrum import keystore, mnemonic from electrum import bitcoin from electrum.mnemonic import is_any_2fa_seed_type +if TYPE_CHECKING: + from electrum.daemon import Daemon + from electrum.plugin import Plugins + from electrum.keystore import Hardware_KeyStore + class WizardViewState(NamedTuple): view: Optional[str] @@ -40,7 +45,7 @@ class AbstractWizard: self._current = WizardViewState(None, {}, {}) self._stack = [] # type: List[WizardViewState] - def navmap_merge(self, additional_navmap): + def navmap_merge(self, additional_navmap: dict): # NOTE: only merges one level deep. Deeper dict levels will overwrite for k, v in additional_navmap.items(): if k in self.navmap: @@ -55,7 +60,7 @@ class AbstractWizard: # view params are transient, meant for extra configuration of a view (e.g. info # msg in a generic choice dialog) # exception: stay on this view - def resolve_next(self, view, wizard_data) -> WizardViewState: + def resolve_next(self, view: str, wizard_data: dict) -> WizardViewState: assert view self._logger.debug(f'view={view}') assert view in self.navmap @@ -127,7 +132,7 @@ class AbstractWizard: return self._current # check if this view is the final view - def is_last_view(self, view, wizard_data): + def is_last_view(self, view: str, wizard_data: dict) -> bool: assert view assert view in self.navmap @@ -149,7 +154,7 @@ class AbstractWizard: else: raise Exception(f'last handler for view {view} is not callable nor a bool literal') - def finished(self, wizard_data): + def finished(self, wizard_data: dict): self._logger.debug('finished.') def reset(self): @@ -182,7 +187,7 @@ class AbstractWizard: return result return sanitize(_stack_item) - def get_wizard_data(self): + def get_wizard_data(self) -> dict: return copy.deepcopy(self._current.wizard_data) @@ -190,7 +195,7 @@ class NewWalletWizard(AbstractWizard): _logger = get_logger(__name__) - def __init__(self, daemon, plugins): + def __init__(self, daemon: 'Daemon', plugins: 'Plugins'): AbstractWizard.__init__(self) self.navmap = { 'wallet_name': { @@ -264,36 +269,36 @@ class NewWalletWizard(AbstractWizard): self._daemon = daemon self.plugins = plugins - def start(self, initial_data=None): + def start(self, initial_data: dict = None) -> WizardViewState: if initial_data is None: initial_data = {} self.reset() self._current = WizardViewState('wallet_name', initial_data, {}) return self._current - def is_single_password(self): + def is_single_password(self) -> bool: raise NotImplementedError() # returns (sub)dict of current cosigner (or root if first) - def current_cosigner(self, wizard_data): + def current_cosigner(self, wizard_data: dict) -> dict: wdata = wizard_data if wizard_data['wallet_type'] == 'multisig' and 'multisig_current_cosigner' in wizard_data: cosigner = wizard_data['multisig_current_cosigner'] wdata = wizard_data['multisig_cosigner_data'][str(cosigner)] return wdata - def needs_derivation_path(self, wizard_data): + def needs_derivation_path(self, wizard_data: dict) -> bool: wdata = self.current_cosigner(wizard_data) return 'seed_variant' in wdata and wdata['seed_variant'] in ['bip39', 'slip39'] - def wants_ext(self, wizard_data): + def wants_ext(self, wizard_data: dict) -> bool: wdata = self.current_cosigner(wizard_data) return 'seed_variant' in wdata and wdata['seed_extend'] - def is_multisig(self, wizard_data): + def is_multisig(self, wizard_data: dict) -> bool: return wizard_data['wallet_type'] == 'multisig' - def on_wallet_type(self, wizard_data): + def on_wallet_type(self, wizard_data: dict) -> str: t = wizard_data['wallet_type'] return { 'standard': 'keystore_type', @@ -302,7 +307,7 @@ class NewWalletWizard(AbstractWizard): 'imported': 'imported' }.get(t) - def on_keystore_type(self, wizard_data): + def on_keystore_type(self, wizard_data: dict) -> str: t = wizard_data['keystore_type'] return { 'createseed': 'create_seed', @@ -311,19 +316,19 @@ class NewWalletWizard(AbstractWizard): 'hardware': 'choose_hardware_device' }.get(t) - def is_hardware(self, wizard_data): + def is_hardware(self, wizard_data: dict) -> bool: return wizard_data['keystore_type'] == 'hardware' - def wallet_password_view(self, wizard_data): + def wallet_password_view(self, wizard_data: dict) -> str: return 'wallet_password_hardware' if self.is_hardware(wizard_data) else 'wallet_password' - def on_hardware_device(self, wizard_data): + def on_hardware_device(self, wizard_data: dict) -> str: _type, _info = wizard_data['hardware_device'] run_hook('init_wallet_wizard', self) plugin = self.plugins.get_plugin(_type) return plugin.wizard_entry_for_device(_info) - def on_have_or_confirm_seed(self, wizard_data): + def on_have_or_confirm_seed(self, wizard_data: dict) -> str: if self.needs_derivation_path(wizard_data): return 'script_and_derivation' elif self.is_multisig(wizard_data): @@ -331,7 +336,7 @@ class NewWalletWizard(AbstractWizard): else: return 'wallet_password' - def maybe_master_pubkey(self, wizard_data): + def maybe_master_pubkey(self, wizard_data: dict): self._logger.debug('maybe_master_pubkey') if self.needs_derivation_path(wizard_data) and 'derivation_path' not in wizard_data: self._logger.debug('deferred, missing derivation_path') @@ -339,7 +344,7 @@ class NewWalletWizard(AbstractWizard): wizard_data['multisig_master_pubkey'] = self.keystore_from_data(wizard_data['wallet_type'], wizard_data).get_master_public_key() - def on_cosigner_keystore_type(self, wizard_data): + def on_cosigner_keystore_type(self, wizard_data: dict) -> str: t = wizard_data['cosigner_keystore_type'] return { 'key': 'multisig_cosigner_key', @@ -347,7 +352,7 @@ class NewWalletWizard(AbstractWizard): 'hardware': 'multisig_cosigner_hardware' }.get(t) - def on_have_cosigner_seed(self, wizard_data): + def on_have_cosigner_seed(self, wizard_data: dict) -> str: current_cosigner = self.current_cosigner(wizard_data) if self.needs_derivation_path(wizard_data) and 'derivation_path' not in current_cosigner: return 'multisig_cosigner_script_and_derivation' @@ -356,7 +361,7 @@ class NewWalletWizard(AbstractWizard): else: return 'multisig_cosigner_keystore' - def last_cosigner(self, wizard_data): + def last_cosigner(self, wizard_data: dict) -> bool: # check if we have the final number of cosigners. Doesn't check if cosigner data itself is complete # (should be validated by wizardcomponents) if not self.is_multisig(wizard_data): @@ -367,7 +372,7 @@ class NewWalletWizard(AbstractWizard): return True - def has_duplicate_masterkeys(self, wizard_data) -> bool: + def has_duplicate_masterkeys(self, wizard_data: dict) -> bool: """Multisig wallets need distinct master keys. If True, need to prevent wallet-creation.""" xpubs = [self.keystore_from_data(wizard_data['wallet_type'], wizard_data).get_master_public_key()] for cosigner in wizard_data['multisig_cosigner_data']: @@ -376,7 +381,7 @@ class NewWalletWizard(AbstractWizard): assert xpubs return len(xpubs) != len(set(xpubs)) - def has_heterogeneous_masterkeys(self, wizard_data) -> bool: + def has_heterogeneous_masterkeys(self, wizard_data: dict) -> bool: """Multisig wallets need homogeneous master keys. All master keys need to be bip32, and e.g. Ypub cannot be mixed with Zpub. If True, need to prevent wallet-creation. @@ -399,7 +404,7 @@ class NewWalletWizard(AbstractWizard): return True return False - def keystore_from_data(self, wallet_type, data): + def keystore_from_data(self, wallet_type: str, data: dict): if 'seed' in data: if data['seed_variant'] == 'electrum': return keystore.from_seed(data['seed'], data['seed_extra_words'], True) @@ -426,7 +431,7 @@ class NewWalletWizard(AbstractWizard): else: raise Exception('no seed or master_key in data') - def is_current_cosigner_hardware(self, wizard_data): + def is_current_cosigner_hardware(self, wizard_data: dict) -> bool: cosigner_data = self.current_cosigner(wizard_data) cosigner_is_hardware = cosigner_data == wizard_data and wizard_data['keystore_type'] == 'hardware' if 'cosigner_keystore_type' in wizard_data and wizard_data['cosigner_keystore_type'] == 'hardware': @@ -467,7 +472,7 @@ class NewWalletWizard(AbstractWizard): return multisig_keys_valid, user_info - def validate_seed(self, seed, seed_variant, wallet_type): + def validate_seed(self, seed: str, seed_variant: str, wallet_type: str): seed_type = '' seed_valid = False validation_message = '' @@ -506,7 +511,7 @@ class NewWalletWizard(AbstractWizard): return seed_valid, seed_type, validation_message - def create_storage(self, path, data): + def create_storage(self, path: str, data: dict): assert data['wallet_type'] in ['standard', '2fa', 'imported', 'multisig'] if os.path.exists(path): @@ -635,7 +640,7 @@ class NewWalletWizard(AbstractWizard): db.load_plugins() db.write() - def hw_keystore(self, data): + def hw_keystore(self, data: dict) -> 'Hardware_KeyStore': return hardware_keystore({ 'type': 'hardware', 'hw_type': data['hw_type'], @@ -673,7 +678,7 @@ class ServerConnectWizard(AbstractWizard): } self._daemon = daemon - def do_configure_proxy(self, wizard_data): + def do_configure_proxy(self, wizard_data: dict): proxy_settings = wizard_data['proxy'] if not self._daemon.network: self._logger.debug('not configuring proxy, electrum config wants offline mode') @@ -685,7 +690,7 @@ class ServerConnectWizard(AbstractWizard): net_params = net_params._replace(proxy=proxy_settings) self._daemon.network.run_from_another_thread(self._daemon.network.set_parameters(net_params)) - def do_configure_server(self, wizard_data): + def do_configure_server(self, wizard_data: dict): self._logger.debug(f'configuring server: {wizard_data!r}') net_params = self._daemon.network.get_parameters() try: @@ -697,12 +702,12 @@ class ServerConnectWizard(AbstractWizard): net_params = net_params._replace(server=server, auto_connect=wizard_data['autoconnect']) self._daemon.network.run_from_another_thread(self._daemon.network.set_parameters(net_params)) - def do_configure_autoconnect(self, wizard_data): + def do_configure_autoconnect(self, wizard_data: dict): self._logger.debug(f'configuring autoconnect: {wizard_data!r}') if self._daemon.config.cv.NETWORK_AUTO_CONNECT.is_modifiable(): self._daemon.config.NETWORK_AUTO_CONNECT = wizard_data['autoconnect'] - def start(self, initial_data=None): + def start(self, initial_data: dict = None) -> WizardViewState: if initial_data is None: initial_data = {} self.reset()