From ab9c3bb9b46e6fa6a50b1d2a20eef24cc77ae7de Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Fri, 24 Nov 2023 15:06:23 +0100 Subject: [PATCH 1/9] qt: avoid potential proxy settings deserialization problems (fixes #8652) --- electrum/gui/qt/network_dialog.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/electrum/gui/qt/network_dialog.py b/electrum/gui/qt/network_dialog.py index 07a9fa308012..fa875bb0d726 100644 --- a/electrum/gui/qt/network_dialog.py +++ b/electrum/gui/qt/network_dialog.py @@ -33,7 +33,7 @@ from PyQt5.QtWidgets import (QTreeWidget, QTreeWidgetItem, QMenu, QGridLayout, QComboBox, QLineEdit, QDialog, QVBoxLayout, QHeaderView, QCheckBox, QTabWidget, QWidget, QLabel) -from PyQt5.QtGui import QFontMetrics +from PyQt5.QtGui import QIntValidator from electrum.i18n import _ from electrum import constants, blockchain, util @@ -53,6 +53,7 @@ protocol_names = ['TCP', 'SSL'] protocol_letters = 'ts' + class NetworkDialog(QDialog, QtEventListener): def __init__(self, *, network: Network, config: 'SimpleConfig'): QDialog.__init__(self) @@ -243,6 +244,9 @@ def __init__(self, network: Network, config: 'SimpleConfig', wizard=False): self.proxy_host.setFixedWidth(fixed_width_hostname) self.proxy_port = QLineEdit() self.proxy_port.setFixedWidth(fixed_width_port) + self.proxy_port_validator = QIntValidator(1, 65535) + self.proxy_port.setValidator(self.proxy_port_validator) + self.proxy_user = QLineEdit() self.proxy_user.setPlaceholderText(_("Proxy user")) self.proxy_password = PasswordLineEdit() @@ -427,6 +431,10 @@ def set_server(self): def set_proxy(self): net_params = self.network.get_parameters() if self.proxy_cb.isChecked(): + if not self.proxy_port.hasAcceptableInput(): + return + if ':' in self.proxy_host.text(): # avoid deserialization pitfall + return proxy = {'mode':str(self.proxy_mode.currentText()).lower(), 'host':str(self.proxy_host.text()), 'port':str(self.proxy_port.text()), From affe3630b01f6d468405ee7bbb9bb48d82f1d57e Mon Sep 17 00:00:00 2001 From: SomberNight Date: Fri, 24 Nov 2023 19:31:36 +0000 Subject: [PATCH 2/9] qt: addr/coins tab: show tooltip for "freeze address" related https://github.com/spesmilo/electrum/issues/8698 --- electrum/gui/messages.py | 3 +++ electrum/gui/qt/address_list.py | 13 +++++++++---- electrum/gui/qt/utxo_list.py | 25 +++++++++++++++++-------- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/electrum/gui/messages.py b/electrum/gui/messages.py index 407b2ed3e771..922679efebd2 100644 --- a/electrum/gui/messages.py +++ b/electrum/gui/messages.py @@ -44,3 +44,6 @@ def to_rtf(msg): """This channel is with a non-trampoline node; it cannot be used if trampoline is enabled. If you want to keep using this channel, you need to disable trampoline routing in your preferences.""" ) + +MSG_FREEZE_ADDRESS = _("When you freeze an address, the funds in that address will not be used for sending bitcoins.") +MSG_FREEZE_COIN = _("When you freeze a coin, it will not be used for sending bitcoins.") diff --git a/electrum/gui/qt/address_list.py b/electrum/gui/qt/address_list.py index 8b61114bb921..20c92b89d982 100644 --- a/electrum/gui/qt/address_list.py +++ b/electrum/gui/qt/address_list.py @@ -40,6 +40,7 @@ from .util import MONOSPACE_FONT, ColorScheme, webopen from .my_treeview import MyTreeView, MySortModel +from ..messages import MSG_FREEZE_ADDRESS if TYPE_CHECKING: from .main_window import ElectrumWindow @@ -284,6 +285,7 @@ def create_menu(self, position): multi_select = len(selected) > 1 addrs = [self.item_from_index(item).text() for item in selected] menu = QMenu() + menu.setToolTipsVisible(True) if not multi_select: idx = self.indexAt(position) if not idx.isValid(): @@ -311,14 +313,17 @@ def create_menu(self, position): menu.addAction(_("View on block explorer"), lambda: webopen(addr_URL)) if not self.wallet.is_frozen_address(addr): - menu.addAction(_("Freeze"), lambda: self.main_window.set_frozen_state_of_addresses([addr], True)) + act = menu.addAction(_("Freeze"), lambda: self.main_window.set_frozen_state_of_addresses([addr], True)) else: - menu.addAction(_("Unfreeze"), lambda: self.main_window.set_frozen_state_of_addresses([addr], False)) + act = menu.addAction(_("Unfreeze"), lambda: self.main_window.set_frozen_state_of_addresses([addr], False)) + act.setToolTip(MSG_FREEZE_ADDRESS) else: # multiple items selected - menu.addAction(_("Freeze"), lambda: self.main_window.set_frozen_state_of_addresses(addrs, True)) - menu.addAction(_("Unfreeze"), lambda: self.main_window.set_frozen_state_of_addresses(addrs, False)) + act = menu.addAction(_("Freeze"), lambda: self.main_window.set_frozen_state_of_addresses(addrs, True)) + act.setToolTip(MSG_FREEZE_ADDRESS) + act = menu.addAction(_("Unfreeze"), lambda: self.main_window.set_frozen_state_of_addresses(addrs, False)) + act.setToolTip(MSG_FREEZE_ADDRESS) coins = self.wallet.get_spendable_coins(addrs) if coins: diff --git a/electrum/gui/qt/utxo_list.py b/electrum/gui/qt/utxo_list.py index 5e10af704556..cddfc1bddb6a 100644 --- a/electrum/gui/qt/utxo_list.py +++ b/electrum/gui/qt/utxo_list.py @@ -40,6 +40,7 @@ from .util import ColorScheme, MONOSPACE_FONT, EnterButton from .my_treeview import MyTreeView from .new_channel_dialog import NewChannelDialog +from ..messages import MSG_FREEZE_ADDRESS, MSG_FREEZE_COIN if TYPE_CHECKING: from .main_window import ElectrumWindow @@ -317,28 +318,36 @@ def create_menu(self, position): utxo = coins[0] addr = utxo.address menu_freeze = menu.addMenu(_("Freeze")) + menu_freeze.setToolTipsVisible(True) if not self.wallet.is_frozen_coin(utxo): - menu_freeze.addAction(_("Freeze Coin"), lambda: self.main_window.set_frozen_state_of_coins([utxo], True)) + act = menu_freeze.addAction(_("Freeze Coin"), lambda: self.main_window.set_frozen_state_of_coins([utxo], True)) else: - menu_freeze.addAction(_("Unfreeze Coin"), lambda: self.main_window.set_frozen_state_of_coins([utxo], False)) + act = menu_freeze.addAction(_("Unfreeze Coin"), lambda: self.main_window.set_frozen_state_of_coins([utxo], False)) + act.setToolTip(MSG_FREEZE_COIN) if not self.wallet.is_frozen_address(addr): - menu_freeze.addAction(_("Freeze Address"), lambda: self.main_window.set_frozen_state_of_addresses([addr], True)) + act = menu_freeze.addAction(_("Freeze Address"), lambda: self.main_window.set_frozen_state_of_addresses([addr], True)) else: - menu_freeze.addAction(_("Unfreeze Address"), lambda: self.main_window.set_frozen_state_of_addresses([addr], False)) + act = menu_freeze.addAction(_("Unfreeze Address"), lambda: self.main_window.set_frozen_state_of_addresses([addr], False)) + act.setToolTip(MSG_FREEZE_ADDRESS) elif len(coins) > 1: # multiple items selected menu.addSeparator() addrs = [utxo.address for utxo in coins] is_coin_frozen = [self.wallet.is_frozen_coin(utxo) for utxo in coins] is_addr_frozen = [self.wallet.is_frozen_address(utxo.address) for utxo in coins] menu_freeze = menu.addMenu(_("Freeze")) + menu_freeze.setToolTipsVisible(True) if not all(is_coin_frozen): - menu_freeze.addAction(_("Freeze Coins"), lambda: self.main_window.set_frozen_state_of_coins(coins, True)) + act = menu_freeze.addAction(_("Freeze Coins"), lambda: self.main_window.set_frozen_state_of_coins(coins, True)) + act.setToolTip(MSG_FREEZE_COIN) if any(is_coin_frozen): - menu_freeze.addAction(_("Unfreeze Coins"), lambda: self.main_window.set_frozen_state_of_coins(coins, False)) + act = menu_freeze.addAction(_("Unfreeze Coins"), lambda: self.main_window.set_frozen_state_of_coins(coins, False)) + act.setToolTip(MSG_FREEZE_COIN) if not all(is_addr_frozen): - menu_freeze.addAction(_("Freeze Addresses"), lambda: self.main_window.set_frozen_state_of_addresses(addrs, True)) + act = menu_freeze.addAction(_("Freeze Addresses"), lambda: self.main_window.set_frozen_state_of_addresses(addrs, True)) + act.setToolTip(MSG_FREEZE_ADDRESS) if any(is_addr_frozen): - menu_freeze.addAction(_("Unfreeze Addresses"), lambda: self.main_window.set_frozen_state_of_addresses(addrs, False)) + act = menu_freeze.addAction(_("Unfreeze Addresses"), lambda: self.main_window.set_frozen_state_of_addresses(addrs, False)) + act.setToolTip(MSG_FREEZE_ADDRESS) menu.exec_(self.viewport().mapToGlobal(position)) From b18f9570fc415569d072132ba96774bd0dc96e84 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Fri, 24 Nov 2023 20:48:04 +0000 Subject: [PATCH 3/9] qml wizard: fix creating imported wallet from camera: concat with space The parser expects the list of keys/addrs to be whitespace-separated (no commas). Same as line 61. --- electrum/gui/qml/components/wizard/WCImport.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/electrum/gui/qml/components/wizard/WCImport.qml b/electrum/gui/qml/components/wizard/WCImport.qml index 06238a3b9501..b185a1c0df99 100644 --- a/electrum/gui/qml/components/wizard/WCImport.qml +++ b/electrum/gui/qml/components/wizard/WCImport.qml @@ -79,7 +79,7 @@ WizardComponent { dialog.onFound.connect(function() { if (verify(dialog.scanData)) { if (import_ta.text != '') - import_ta.text = import_ta.text + ',\n' + import_ta.text = import_ta.text + '\n' import_ta.text = import_ta.text + dialog.scanData } dialog.close() From ff53925811e1f0c6b276a4650d0239db4d8353c1 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Fri, 24 Nov 2023 22:36:37 +0000 Subject: [PATCH 4/9] qt console: fix tracebacks in windows binaries fixes https://github.com/spesmilo/electrum/issues/3315 The cause was that tracebacks look different whether stack items have source text available. When using the pyinstaller windows binary, there is no source text available. Example when running from source: ``` >>> a Traceback (most recent call last): File "...\electrum\gui\qt\console.py", line 256, in exec_command result = eval(command, self.namespace, self.namespace) File "", line 1, in NameError: name 'a' is not defined ``` Example for pyinstaller windows binary: ``` >>> a Traceback (most recent call last): File "electrum\gui\qt\console.py", line 256, in exec_command File "", line 1, in NameError: name 'a' is not defined ``` --- electrum/gui/qt/console.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/electrum/gui/qt/console.py b/electrum/gui/qt/console.py index a3fd20545274..c05cc43a0a09 100644 --- a/electrum/gui/qt/console.py +++ b/electrum/gui/qt/console.py @@ -264,12 +264,18 @@ def write(self, text): exec(command, self.namespace, self.namespace) except SystemExit: self.close() - except BaseException: - traceback_lines = traceback.format_exc().split('\n') - # Remove traceback mentioning this file, and a linebreak - for i in (3,2,1,-1): - traceback_lines.pop(i) - self.appendPlainText('\n'.join(traceback_lines)) + except BaseException as e: + te = traceback.TracebackException.from_exception(e) + # rm part of traceback mentioning this file. + # (note: we rm stack items before converting to str, instead of removing lines from the str, + # as this is more reliable. The latter would differ whether the traceback has source text lines, + # which is not always the case.) + te.stack = traceback.StackSummary.from_list(te.stack[1:]) + tb_str = "".join(te.format()) + # rm last linebreak: + if tb_str.endswith("\n"): + tb_str = tb_str[:-1] + self.appendPlainText(tb_str) sys.stdout = tmp_stdout def keyPressEvent(self, event): From c16b83f46aeae4ef8172b2e48666bb9c97450054 Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Mon, 27 Nov 2023 14:44:22 +0100 Subject: [PATCH 5/9] tests: add test verifying if privkey add to existing imported wallet persists correctly. --- electrum/tests/test_wallet.py | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/electrum/tests/test_wallet.py b/electrum/tests/test_wallet.py index 0fd469e0ac88..e6252b716691 100644 --- a/electrum/tests/test_wallet.py +++ b/electrum/tests/test_wallet.py @@ -84,6 +84,42 @@ def test_write_dictionary_to_file(self): for key, value in some_dict.items(): self.assertEqual(d[key], value) + async def test_storage_imported_add_privkeys_persistence_test(self): + text = ' '.join([ + 'p2wpkh:L4jkdiXszG26SUYvwwJhzGwg37H2nLhrbip7u6crmgNeJysv5FHL', + 'p2wpkh:L24GxnN7NNUAfCXA6hFzB1jt59fYAAiFZMcLaJ2ZSawGpM3uqhb1' + ]) + d = restore_wallet_from_text(text, path=self.wallet_path, config=self.config) + wallet = d['wallet'] # type: Imported_Wallet + addr0 = wallet.get_receiving_addresses()[0] + self.assertEqual('bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw', addr0) + self.assertEqual('p2wpkh:L4jkdiXszG26SUYvwwJhzGwg37H2nLhrbip7u6crmgNeJysv5FHL', + wallet.export_private_key(addr0, password=None)) + self.assertEqual(2, len(wallet.get_receiving_addresses())) + + wallet.save_db() + + # open the wallet anew again, and add a privkey. This should add the new data as a json_patch + wallet = None + storage = WalletStorage(self.wallet_path) + db = WalletDB(storage.read(), storage=storage, upgrade=True) + wallet = Wallet(db, config=self.config) + + wallet.import_private_keys(['p2wpkh:KzuqaaLp9zYjVuj8vQtCwFdiZFreW3NJNBachgVS8S9XMgj5y78b'], password=None) + wallet.save_db() + + # open the wallet anew again, and verify if the privkey was stored + wallet = None + storage = WalletStorage(self.wallet_path) + db = WalletDB(storage.read(), storage=storage, upgrade=True) + wallet = Wallet(db, config=self.config) + self.assertEqual(3, len(wallet.get_receiving_addresses())) + self.assertEqual(3, len(wallet.keystore.keypairs)) + self.assertTrue('03bf450797034dc95693096e575e3b3db14e5f074679b349b727f90fc7804ce7ab' in wallet.keystore.keypairs) + self.assertTrue('030dac677b9484e23db6f9255eddf433f4f12c02f9b35e0100f2f103ffbccf540f' in wallet.keystore.keypairs) + self.assertTrue('02f11d5f222a728fd08226cb5a1e85a74d58fc257bd3764bf1234346f91defed72' in wallet.keystore.keypairs) + + class FakeExchange(ExchangeBase): def __init__(self, rate): super().__init__(lambda self: None, lambda self: None) From 6efd1fc768c654b36eae2239298bd8f3852f3cf0 Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Mon, 27 Nov 2023 14:49:20 +0100 Subject: [PATCH 6/9] followup prev --- electrum/tests/test_wallet.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/electrum/tests/test_wallet.py b/electrum/tests/test_wallet.py index e6252b716691..53046fd941bb 100644 --- a/electrum/tests/test_wallet.py +++ b/electrum/tests/test_wallet.py @@ -91,10 +91,6 @@ async def test_storage_imported_add_privkeys_persistence_test(self): ]) d = restore_wallet_from_text(text, path=self.wallet_path, config=self.config) wallet = d['wallet'] # type: Imported_Wallet - addr0 = wallet.get_receiving_addresses()[0] - self.assertEqual('bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw', addr0) - self.assertEqual('p2wpkh:L4jkdiXszG26SUYvwwJhzGwg37H2nLhrbip7u6crmgNeJysv5FHL', - wallet.export_private_key(addr0, password=None)) self.assertEqual(2, len(wallet.get_receiving_addresses())) wallet.save_db() @@ -106,6 +102,8 @@ async def test_storage_imported_add_privkeys_persistence_test(self): wallet = Wallet(db, config=self.config) wallet.import_private_keys(['p2wpkh:KzuqaaLp9zYjVuj8vQtCwFdiZFreW3NJNBachgVS8S9XMgj5y78b'], password=None) + self.assertEqual(3, len(wallet.get_receiving_addresses())) + self.assertEqual(3, len(wallet.keystore.keypairs)) wallet.save_db() # open the wallet anew again, and verify if the privkey was stored From 85f13cc6ea23d3410818cd3d6b3488a5d44908ea Mon Sep 17 00:00:00 2001 From: ThomasV Date: Mon, 27 Nov 2023 15:09:14 +0100 Subject: [PATCH 7/9] load_keystore: deepcopy object so that it differs from the one in db.data db.data should not be modified directly --- electrum/keystore.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/electrum/keystore.py b/electrum/keystore.py index f4d4a2188334..72f5fb602f90 100644 --- a/electrum/keystore.py +++ b/electrum/keystore.py @@ -27,6 +27,7 @@ from unicodedata import normalize import hashlib import re +import copy from typing import Tuple, TYPE_CHECKING, Union, Sequence, Optional, Dict, List, NamedTuple from functools import lru_cache, wraps from abc import ABC, abstractmethod @@ -1069,7 +1070,8 @@ def hardware_keystore(d) -> Hardware_KeyStore: f'hw_keystores: {list(hw_keystores)}') def load_keystore(db: 'WalletDB', name: str) -> KeyStore: - d = db.get(name, {}) + # deepcopy object to avoid keeping a pointer to db.data + d = copy.deepcopy(db.get(name, {})) t = d.get('type') if not t: raise WalletFileException( From ae9767880e855ba47fe663c3619e9f481d4c1854 Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Mon, 27 Nov 2023 16:14:41 +0100 Subject: [PATCH 8/9] ui: add icons for script type and deterministic wallet --- electrum/gui/icons/hd.png | Bin 0 -> 2223 bytes electrum/gui/icons/hd_white.png | Bin 0 -> 2194 bytes electrum/gui/icons/script.png | Bin 0 -> 2911 bytes electrum/gui/icons/script_white.png | Bin 0 -> 2910 bytes electrum/gui/qml/components/WalletDetails.qml | 2 ++ 5 files changed, 2 insertions(+) create mode 100644 electrum/gui/icons/hd.png create mode 100644 electrum/gui/icons/hd_white.png create mode 100644 electrum/gui/icons/script.png create mode 100644 electrum/gui/icons/script_white.png diff --git a/electrum/gui/icons/hd.png b/electrum/gui/icons/hd.png new file mode 100644 index 0000000000000000000000000000000000000000..1d58a3645f802a0125fbd30ee14379e352324e07 GIT binary patch literal 2223 zcmb_eX;hL47k<%F6hu)cF!$F5w;{?(4fh}=6E_%fOI*_2aUnD}GBZUTM~|AOX68;z zxu<0g$C|WauuN=h$}qL25x3IRT;{X+{>=aH`*H6*=RS8i=RQB4o99k;Qjp&-4*-Ay z$(i7}8`<9sDzob!*akh_4RQ=;pF{vaJosjip}3aN-K1I)k(T5Y7nZ~dN;m_sSS)Nr zY*b=M5aSFsE+M>X!FoRcK&?px2TF4Fd`+rAlc;vNwy502*ogGAqr;|CVT(@Wfb%n7 zJunz9)u?}!1`kfDzmZ2Pi#!Q;EfmSrqsRI|Lm7wu?^+@Xm3Jm7$5l#Md_RKOV2OQRGEG+vy6}kH7^jABpFFRiQtAHgu0TQ(^mQC65L9 zA6HjLzZj&hf?}TkDpRmj%3xP=?}kZ8qZ7K|M8bQ26hmvNxL~!gN{uf{znm*Rs8a<@ z?mcCPnBkc<13nk6rgfVE6}7p9CsxrTfG~O3#9rt?_h~a{XNE^(WG#S2t*_CFOINiz zGz6!`eHVAOVPXMRU0`hQgIa3Iwf64#8AV~1{v=D7i3?Hmy1-q~UT$YWa?q>w2@v>{ zl%*E~0)M{I*bQSOPX~PCCtM!O)=h)cH5R3IPyP=+6#`5rqrOpCnFXUmwWZNR_jc=i z_MY8dlcF#o3}leUu&I;k#y$qV&4=)%E7DAwd)|zJVF;f!-RG0Q(K&6lyGRde$`i`k zA~ftMp_nBqy(ahd+n)O;q+0$uC4RuiuwQ_zKjgOHGy~ADL_OvLS4+`f${$({)9~|5 zvRm?_YcA;_Q-ZDK9H<`vQ?{bifx_4GNHLyoL71^~edW6EI!(G)#PULyg;L%Iaad zdC(gIRtQWUM8n73$m9jiMBNvY>*&%gb}P&{fg#mKrUusDbfj)8Ogav*ppVZ5PRLsOXSww zYELcFg&zvMh8cAhf^bAstExTq?W5#z!3ZWTzR*LIlD)IiVwU%0gTqAxIyNyBrc;U^ zHFEEE-EwaQxW^pO^F>jj#b9cBRQ|SivF&KwAY7dD&eKwkYNNIuy5ro)yEP}z6#^*f zIAK+D`H8pzchO6!E3<{3k6Zb(3SaJ(e@ayfz-jk(aP7+g@0s6}e|4nBbU3ahW(U5v z>}x{nIMs1G*=wwd=7J(>jK+qxy9fn|TLfMApqf;F5b4R?aUJ^2xM6j65nL=InZYM{F&AoY3y*FT-*-~N2*k?|52ZmLCfDWqz`+x zMkp>tZdVIEA1;>+C!`M$=Z8BK&IGP_?G7?%+&+zA0Nq6#VyO1g3c90oIdmfD5M<}M z$4}^U211qf)Ns;Y6Iif4&VKH5MY&e}E;hQ0?Ode|jC4{@{-K!kZFz{VAzx-tqtBLB zOUp<^>4JUuI>`v$#l!96K}}=(c=^fK)T{er-OMsAmZDJ(pmQ-(Z|Y3xbeQSVW=A2l ze!J>Zc*lRFHkwZ~><0;|cl}5ESYT(@zgvQ#LGZm|W++v*1?| z^zY_JbR3o(8l)^Yxxp2c!WOZp;vw~Mk9;mx-~Vw}x^J&KO&6>wxI|gqGq~KJ#kqX+ zhy*MU{m{sh&w8JKJY0p`?XCQGMWS7s9L*T~#U-{2LKNPFO5dKM_An)~u?915N~~U~ zQdX(_NXM5-`0jL6kk#+q;OL)~&NpYLer*`Ols!8Yu51`yO{gG$fiJm6+$ z_>k|7#>MGu1_R}>-Max9RH*VJ&I}xUh|dl_BuHhiDTir~NR##9v3HcUB(I%lU(b2x z-$d#>*o2&@<2p58<2)j^!#9(5Fmq;Nxh$R{XSm+xkvi$%GBZ%5dA}(MWSgh|nfkuy ih53KQ`QK72%!C{%`w;4RnFZf{HUJWlOt|Gp&-xGe+~LOn literal 0 HcmV?d00001 diff --git a/electrum/gui/icons/hd_white.png b/electrum/gui/icons/hd_white.png new file mode 100644 index 0000000000000000000000000000000000000000..570520307b4bf517665e29657bbab236053faf1a GIT binary patch literal 2194 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 literal 0 HcmV?d00001 diff --git a/electrum/gui/qml/components/WalletDetails.qml b/electrum/gui/qml/components/WalletDetails.qml index d5e2b22ee2b2..c34ba1b164de 100644 --- a/electrum/gui/qml/components/WalletDetails.qml +++ b/electrum/gui/qml/components/WalletDetails.qml @@ -73,6 +73,7 @@ Pane { text: Daemon.currentWallet.txinType font.pixelSize: constants.fontSizeSmall font.bold: true + iconSource: '../../../icons/script_white.png' } Tag { Layout.alignment: Qt.AlignHCenter @@ -80,6 +81,7 @@ Pane { visible: Daemon.currentWallet.isDeterministic font.pixelSize: constants.fontSizeSmall font.bold: true + iconSource: '../../../icons/hd_white.png' } Tag { Layout.alignment: Qt.AlignHCenter From 6467db0b7db6a3ed3d8403cf24a10dbfe1c6913c Mon Sep 17 00:00:00 2001 From: SomberNight Date: Mon, 27 Nov 2023 15:30:26 +0000 Subject: [PATCH 9/9] json_db: rename "_write" to sth more descriptive --- electrum/json_db.py | 12 ++++++------ electrum/keystore.py | 1 + electrum/wallet.py | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/electrum/json_db.py b/electrum/json_db.py index 38481575cfcc..1c1841bd809b 100644 --- a/electrum/json_db.py +++ b/electrum/json_db.py @@ -226,7 +226,7 @@ def __init__(self, s: str, storage=None, encoder=None, upgrader=None): self.data = StoredDict(data, self, []) # write file in case there was a db upgrade if self.storage and self.storage.file_exists(): - self._write() + self.write_and_force_consolidation() def load_data(self, s:str) -> dict: """ overloaded in wallet_db """ @@ -381,10 +381,10 @@ def _convert_value(self, path, key, v): @locked def write(self): - if not self.storage.file_exists()\ - or self.storage.is_encrypted()\ - or self.storage.needs_consolidation(): - self._write() + if (not self.storage.file_exists() + or self.storage.is_encrypted() + or self.storage.needs_consolidation()): + self.write_and_force_consolidation() else: self._append_pending_changes() @@ -402,7 +402,7 @@ def _append_pending_changes(self): @locked @profiler - def _write(self): + def write_and_force_consolidation(self): if threading.current_thread().daemon: raise Exception('daemon thread cannot write db') if not self.modified(): diff --git a/electrum/keystore.py b/electrum/keystore.py index 72f5fb602f90..04529ca17a46 100644 --- a/electrum/keystore.py +++ b/electrum/keystore.py @@ -1071,6 +1071,7 @@ def hardware_keystore(d) -> Hardware_KeyStore: def load_keystore(db: 'WalletDB', name: str) -> KeyStore: # deepcopy object to avoid keeping a pointer to db.data + # note: this is needed as type(wallet.db.get("keystore")) != StoredDict d = copy.deepcopy(db.get(name, {})) t = d.get('type') if not t: diff --git a/electrum/wallet.py b/electrum/wallet.py index 76ac6a55b6ec..96ff5e2cb098 100644 --- a/electrum/wallet.py +++ b/electrum/wallet.py @@ -2864,9 +2864,9 @@ def update_password(self, old_pw, new_pw, *, encrypt_storage: bool = True): self._update_password_for_keystore(old_pw, new_pw) encrypt_keystore = self.can_have_keystore_encryption() self.db.set_keystore_encryption(bool(new_pw) and encrypt_keystore) - ## save changes + # save changes. force full rewrite to rm remnants of old password if self.storage and self.storage.file_exists(): - self.db._write() + self.db.write_and_force_consolidation() # if wallet was previously unlocked, update password in memory if self._password_in_memory is not None: self._password_in_memory = new_pw