diff --git a/electrum/gui/qml/qedaemon.py b/electrum/gui/qml/qedaemon.py index 311eebc18..48d36bdd5 100644 --- a/electrum/gui/qml/qedaemon.py +++ b/electrum/gui/qml/qedaemon.py @@ -231,7 +231,7 @@ class QEDaemon(AuthMixin, QObject): return if self.daemon.config.WALLET_SHOULD_USE_SINGLE_PASSWORD: - self._use_single_password = self.daemon.update_password_for_directory(old_password=local_password, new_password=local_password) + self._use_single_password = self._update_password_for_directory_and_unlock_wallets(old_password=local_password, new_password=local_password) if not self._use_single_password and self.daemon.config.WALLET_ANDROID_USE_BIOMETRIC_AUTHENTICATION: # we need to disable biometric auth if the user creates wallets with different passwords as # we only store one encrypted password which is not associated to a specific wallet @@ -399,11 +399,24 @@ class QEDaemon(AuthMixin, QObject): def setPassword(self, password): assert self._use_single_password assert password - if not self.daemon.update_password_for_directory(old_password=self._password, new_password=password): + if not self._update_password_for_directory_and_unlock_wallets(old_password=self._password, new_password=password): return False self._password = password return True + def _update_password_for_directory_and_unlock_wallets(self, *, old_password, new_password): + # note: this assumes all wallet files are in a single directory. + # change wallet passwords: + ret = self.daemon.update_password_for_directory(old_password=old_password, new_password=new_password) + # If some wallets just had their password changed, they got "locked" by wallet.update_password(). + # If the password is not unified yet, other loaded wallets might still be unlocked. + # restore the invariant that all loaded wallets in qml must be unlocked: + for w in self.daemon.get_wallets().values(): + if not w.is_unlocked(): + w.unlock(new_password) + assert w.is_unlocked() + return ret + @pyqtProperty(QENewWalletWizard, notify=newWalletWizardChanged) def newWalletWizard(self): if not self._new_wallet_wizard: diff --git a/electrum/gui/qml/qewallet.py b/electrum/gui/qml/qewallet.py index f08005baf..b2b3a64a3 100644 --- a/electrum/gui/qml/qewallet.py +++ b/electrum/gui/qml/qewallet.py @@ -765,6 +765,7 @@ class QEWallet(AuthMixin, QObject, QtEventListener): try: self._logger.info('setting new password') self.wallet.update_password(current_password, password, encrypt_storage=True) + # restore the invariant that all loaded wallets in qml must be unlocked: self.wallet.unlock(password) return True except InvalidPassword as e: diff --git a/electrum/wallet.py b/electrum/wallet.py index cf7874ff0..c852603af 100644 --- a/electrum/wallet.py +++ b/electrum/wallet.py @@ -3532,16 +3532,27 @@ class Abstract_Wallet(ABC, Logger, EventListener): """Returns the number of new addresses we generated.""" return 0 - def unlock(self, password): + def unlock(self, password: Optional[str]) -> None: self.logger.info(f'unlocking wallet') + password = password or None self.check_password(password) self._password_in_memory = password def lock_wallet(self): self._password_in_memory = None - def get_unlocked_password(self): - return self._password_in_memory + def get_unlocked_password(self) -> Optional[str]: + pw = self._password_in_memory + if not self.is_unlocked(): + return None + try: + self.check_password(pw) + except InvalidPassword as e: + raise Exception("inconsistent _password_in_memory") from e + return pw + + def is_unlocked(self) -> bool: + return self._password_in_memory is not None or not self.has_password() def get_text_not_enough_funds_mentioning_frozen( self,