hww: fix crash when disabling keystore for hww (was unimplemented for Hardware_Keystore) also preserve derivation path and root fingerprint for watch-only keystore.
What changed, and why it matters
This commit fixes a crash in the Electrum Bitcoin wallet that occurred when users tried to disable a hardware wallet keystore. It also ensures that important wallet metadata (the derivation path and root fingerprint) is preserved when converting a wallet to watch-only mode. The change is a defensive bug fix rather than a security vulnerability, but the missing metadata could have caused user confusion or wallet recovery issues.
No urgent security action required. Users running affected versions should update to a version containing this fix if they use hardware wallets or watch-only wallets, to avoid crashes and metadata loss. Developers should verify that no other keystore subclasses lack a `watching_only_keystore()` implementation.
Security signals we found
Fixes a runtime crash in keystore downgrade path
Preserves root fingerprint and derivation prefix during watch-only conversion
Prevents potential wallet metadata loss that could affect recovery or address derivation
Evidence from the diff
The patch adds a watching_only_keystore() implementation to Hardware_KeyStore in electrum/keystore.py, which previously inherited an unimplemented/crashing path. It also updates BIP32_KeyStore.watching_only_keystore() to preserve root_fingerprint and derivation_prefix when downgrading to a watch-only keystore. The change prevents an exception and preserves key metadata needed for correct wallet reconstruction.
Changed components
electrum/keystore.pyBIP32_KeyStore.watching_only_keystore()Hardware_KeyStore.watching_only_keystore()Inspect captured patch +15 / −4
diff --git a/electrum/keystore.py b/electrum/keystore.py
index 8d76536..13241f0 100644
--- a/electrum/keystore.py
+++ b/electrum/keystore.py
@@ -640,7 +640,11 @@ class BIP32_KeyStore(Xpub, Deterministic_KeyStore):
self.xprv = d.get('xprv')
def watching_only_keystore(self):
- return BIP32_KeyStore({'xpub':self.xpub})
+ return BIP32_KeyStore({
+ 'xpub': self.xpub,
+ 'root_fingerprint': self.get_root_fingerprint(),
+ 'derivation_prefix': self.get_derivation_prefix(),
+ })
def format_seed(self, seed):
return ' '.join(seed.split())
@@ -895,6 +899,13 @@ class Hardware_KeyStore(Xpub, KeyStore):
self.handler = None # type: Optional[HardwareHandlerBase]
run_hook('init_keystore', self)
+ def watching_only_keystore(self):
+ return BIP32_KeyStore({
+ 'xpub': self.xpub,
+ 'root_fingerprint': self.get_root_fingerprint(),
+ 'derivation_prefix': self.get_derivation_prefix(),
+ })
+
def set_label(self, label: Optional[str]) -> None:
self.label = label
@@ -914,13 +925,13 @@ class Hardware_KeyStore(Xpub, KeyStore):
'xpub': self.xpub,
'derivation': self.get_derivation_prefix(),
'root_fingerprint': self.get_root_fingerprint(),
- 'label':self.label,
+ 'label': self.label,
'soft_device_id': self.soft_device_id,
}
def is_watching_only(self):
- '''The wallet is not watching-only; the user will be prompted for
- pin and passphrase as appropriate when needed.'''
+ """The wallet is not watching-only; the user will be prompted for
+ pin and passphrase as appropriate when needed."""
assert not self.has_seed()
return False
Why this scored 32/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.