qml: QEDaemon.setPassword to restore invariant wallets are unlocked
What changed, and why it matters
This commit fixes a bug in Electrum's mobile/QML wallet interface where changing a wallet password could leave some loaded wallets in a 'locked' state. The fix restores the app's internal rule that all loaded wallets must stay unlocked. A locked wallet could temporarily block the user from spending or viewing balances until re-unlocked, but this appears to be a reliability/UX bug rather than a direct theft vulnerability.
Treat as a bug fix with minor security/availability relevance. Users on affected mobile builds should update to a version containing this commit. Review whether any other GUI layers (desktop Qt, CLI) have similar locked-state inconsistencies after password changes.
Security signals we found
Fixes a state-consistency bug that could leave wallets unexpectedly locked
Restores an explicit application invariant ('all loaded wallets in qml must be unlocked')
Involves password-change and wallet-unlock logic in the QML/Android GUI layer
No direct cryptographic weakness or remote attack vector visible in the diff
Evidence from the diff
The patch introduces _update_password_for_directory_and_unlock_wallets() in QEDaemon and calls it from setPassword() and the single-password setup path. After update_password_for_directory() changes wallet file encryption passwords, wallet.update_password() can leave wallets locked. The new helper iterates over all loaded wallets and unlocks any that are locked with the new password. A similar unlock() call is added after update_password in QEWallet. This restores the QML-layer invariant that loaded wallets remain unlocked.
Changed components
electrum/gui/qml/qedaemon.pyelectrum/gui/qml/qewallet.pyQML/Android mobile wallet password-change flowSingle-password directory update pathInspect captured patch +16 / −2
diff --git a/electrum/gui/qml/qedaemon.py b/electrum/gui/qml/qedaemon.py
index 311eebc..48d36bd 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 f08005b..b2b3a64 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:
Why this scored 59/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.