1
0
Commit Graph

19102 Commits

Author SHA1 Message Date
SomberNight
463e447439 wallet.can_have_lightning: more explicit impl
Watching-only wallets can have a keystore.
It was actually keystore.may_have_password() that was filtering them out but that was not really obvious.
Better to be explicit I think.
2025-07-18 11:49:50 +00:00
ghost43
6feba10170 Merge pull request #10045 from f321x/fix_swap_update_relays
fix: NostrTransport.update_relays() KeyError
2025-07-18 11:29:05 +00:00
f321x
39bcdec584 fix: handle UserCancelled in show_transaction
`TxDialog` may has to fetch tx information from the network on
construction (`self.set_tx()`) and is showing a `RunCoroutineDialog`.
If the user cancels this dialog the `UserCancelled` exception is not
caught in `show_transaction()` and can even lead to Electrum crashing.
Fixes #10041
2025-07-18 13:14:27 +02:00
f321x
1b325394ba fix: exception when clicking "New Channel"
Fixes exception occuring when `wallet.lnworker` is `None` and the user
clicks on `New Channel` in the channels list tab. The `New Channel`
button is enabled when the wallet *can* have lightning, not the wallet
actually having lightning enabled.
With this patch the `init_lightning_dialog` will show up if the user
clicks on `New Channel` but lightning is not yet enabled.
I noticed this by restoring from a funded vpub and then
loading the keystore from seed afterwards.
2025-07-18 11:12:25 +02:00
f321x
dd41e87295 fix: NostrTransport.update_relays() KeyError
`NostrTransport.update_relays()` raises a `KeyError` when the offer of
the configured swapserver (`config.SWAPSERVER_NPUB`) is not in
`self._offers` even though `sm.pairs_updated` gets triggered. This
happens because `NostrTransport.get_pairs()` called `sm.update_pairs()`
before adding the received offer to `self._offers`.
2025-07-18 10:17:12 +02:00
SomberNight
23adf13070 qml: fix doNotify for Windows. lol
```
319.55 | E | gui.qml.qeapp.Exception_Hook | exception caught by crash reporter
Traceback (most recent call last):
  File "...\electrum\electrum\util.py", line 1130, in run_with_except_hook
    run_original(*args2, **kwargs2)
  File "...\Python310\lib\threading.py", line 953, in run
    self._target(*self._args, **self._kwargs)
  File "...\plyer\platforms\win\libs\balloontip.py", line 206, in balloon_tip
    WindowsBalloonTip(**kwargs)
  File "...\plyer\platforms\win\libs\balloontip.py", line 130, in __init__
    raise Exception('Could not load icon {}'.format(app_icon))
Exception: Could not load icon ...\electrum\electrum\gui\icons\electrum.png
```
2025-07-18 01:37:07 +00:00
SomberNight
e658fa4e04 qml: QETxFinalizer: handle txid being None
fixes https://github.com/spesmilo/electrum/issues/10042
2025-07-18 01:11:19 +00:00
SomberNight
6be5bf96a8 keystore: Old_KeyStore: change type of hex_seed to hex str
instead of b"deadbeef", now it is "deadbeef"
2025-07-18 00:42:09 +00:00
SomberNight
f86bdc86a2 keystore: Old_KeyStore: distinguish between seed and hex_seed 2025-07-18 00:32:52 +00:00
SomberNight
a257072391 keystore: add more type hints
wtf is going on with the type of hex_seed

and mn_encode is polymorphic in a really ugly way...
2025-07-18 00:22:18 +00:00
SomberNight
f6db5fd77c keystore: rm unused methods 2025-07-18 00:09:14 +00:00
ghost43
8770c97c4f Merge pull request #10040 from benma/bitbox02-7.0.0
bitbox02: update to 7.0.0
2025-07-17 12:08:58 +00:00
SomberNight
52f67b50dc build: update pinned bitbox02 (partial rerun freeze_packages) 2025-07-17 12:08:02 +00:00
Marko Bencun
6567e01f61 bitbox02: update to 7.0.0
This adds support for BitBox02 Nova devices.
2025-07-17 12:07:24 +02:00
ThomasV
244a4c5053 update date for 4.6.0 4.6.0 2025-07-16 14:50:11 +02:00
ghost43
c3b9ef0c89 Merge pull request #10034 from f321x/fix_confirm_tx_dialog_timer_exc
fix: qt: handle main_window.gui_object.timer being None
2025-07-16 12:17:24 +00:00
Sander van Grieken
0e055f8127 qt: don't share ElectrumGui.QTimer, use self-contained QTimer so its lifecycle is synced in
`ElectrumWindow`, `TxEditor`, `SwapDialog`

Also use `QTimer` classmethod for `.singleShot()` occurrences.
2025-07-16 13:13:26 +02:00
accumulator
371c1bd1e3 Merge pull request #10036 from accumulator/fix_10035
qml: fix passphrase/seed extension wordwrap
2025-07-16 13:06:44 +02:00
Sander van Grieken
b9ea17674d qml: fix passphrase/seed extension wordwrap 2025-07-16 13:05:44 +02:00
ghost43
b259aab727 Merge pull request #10032 from SomberNight/202507_qt_confirm_txdialog_fee
qt ConfirmTxDialog: also save custom fixed feerate fee_policies
2025-07-16 10:47:28 +00:00
f321x
121b7b767e fix: qt: handle main_window.gui_object.timer being None
When closing Electrum with open `ConfirmTxDialog` the following
exception is raised:

```
1319.20 | E | gui.qt.exception_window.Exception_Hook | exception caught by crash reporter
Traceback (most recent call last):
  File "/home/user/code/electrum-fork/electrum/gui/qt/send_tab.py", line 575, in do_pay_or_get_invoice
    self.do_pay_invoice(self.pending_invoice)
    ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/code/electrum-fork/electrum/gui/qt/send_tab.py", line 602, in do_pay_invoice
    self.pay_onchain_dialog(invoice.outputs, invoice=invoice)
    ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/code/electrum-fork/electrum/gui/qt/send_tab.py", line 328, in pay_onchain_dialog
    tx, is_preview = self.window.confirm_tx_dialog(make_tx, output_value, batching_candidates=candidates)
                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/code/electrum-fork/electrum/gui/qt/main_window.py", line 1502, in confirm_tx_dialog
    return d.run(), d.is_preview
           ~~~~~^^
  File "/home/user/code/electrum-fork/electrum/gui/qt/confirm_tx_dialog.py", line 477, in run
    self.stop_editor_updates()
    ~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "/home/user/code/electrum-fork/electrum/gui/qt/confirm_tx_dialog.py", line 133, in stop_editor_updates
    self.main_window.gui_object.timer.timeout.disconnect(self.timer_actions)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'timeout'
```

This can be prevented by checking if `main_window.gui_object.timer` is
None before trying to disconnect it.
2025-07-16 09:47:32 +02:00
SomberNight
3ceb59d58e interface: parallel header-chunks download
We request chunks concurrently. This makes header-sync much faster
when we are many blocks behind.

notes:
- all chunks are downloaded from the same interface, just for simplicity
- we request up to 10 chunks concurrently (so 10*2016 headers)
  - more chunks: higher memory requirements
  - more chunks: higher concurrency => syncing needs fewer network round-trips
  - if a chunk does not connect, bandwidth for all later chunks is wasted
  - we can tweak the constant or make it dynamic or make it a configvar, etc, later
- without this, we progress the chain tip by around 1 chunk per second
  - 52k blocks (1 year on mainnet) takes around 26 seconds
  - this is probably not *that* interesting for mainnet,
    but for testnet3, that sometimes has 200x the block-rate of mainnet,
    it is extremely useful
2025-07-16 00:15:19 +00:00
SomberNight
6a56fd756b interface: split request_chunk, based on "can_return_early" param 2025-07-15 22:53:35 +00:00
SomberNight
5fad4bff8f verifier: fix off-by-one for max_checkpoint
if a wallet had a tx mined in the max_checkpoint block, in certain cases
we would leave it forever in the "unverified" state and remain stuck in "synchronizing..."
2025-07-15 22:35:21 +00:00
SomberNight
76005372a6 qt ConfirmTxDialog: only show fee_target text if slider is active 2025-07-15 16:52:11 +00:00
SomberNight
02b0073ca5 qt ConfirmTxDialog: also save custom fixed feerate fee_policies
- save custom fixed feerate fee_policies, not just slider-restricted ones
- dynamically update fee_target text, to follow feerate_e
  - otherwise when the slider is set to "feerate", users would be shown two conflicting values
2025-07-15 16:45:29 +00:00
SomberNight
68a203336e qt ConfirmTxDialog: fix feerate_e 2025-07-15 16:43:53 +00:00
SomberNight
dd2fa90838 prepare release 4.6.0 2025-07-15 14:46:41 +00:00
ThomasV
bfba6873f8 Merge pull request #10031 from SomberNight/202507_walletdb_add_configvar_partial_writes_4
wallet_db: add configvar for partial_writes, disable by default
2025-07-15 16:28:33 +02:00
SomberNight
85fc95c71b wallet_db: add configvar for partial_writes, disable by default
Adds a new configvar `WALLET_PARTIAL_WRITES` to enable/disable partial writes for the walletDB.
This is a further restriction on top of the existing restrictions,
e.g. wallet files still need to have file encryption disabled for partial writes.
It defaults to off, so even for unencrypted wallets we disable partial writes for now.

This is used as a stopgap measure until we fix the issues found with the partial writes impl
(see https://github.com/spesmilo/electrum/issues/10000).
2025-07-15 14:03:48 +00:00
ThomasV
6d22c24deb Merge pull request #10028 from SomberNight/202507_issue10021
qt: only allow wallet unlock if wallet has ks-enc
2025-07-15 15:30:33 +02:00
SomberNight
482d573f55 stdio gui: use daemon.load_wallet(), similar to text gui 2025-07-15 13:23:50 +00:00
SomberNight
3c82b00c5e WalletDB() usage: trivial refactors and fixes
split off from https://github.com/spesmilo/electrum/pull/10027
2025-07-15 12:30:06 +00:00
SomberNight
7611d4c3b3 scripts: fix "cannot schedule new futures after interpreter shutdown"
- looks like around python3.9, they changed it so that
  if we don't block on the main thread, it starts to shut things down
- polling thread.join() makes Ctrl+C work. kind of.

```
$ ./electrum/scripts/txradar.py 6bde84a981e72573666fcc51c81ec3f8f4a813709bf16451dce3f106a114d392
Exception in run: RuntimeError('cannot schedule new futures after interpreter shutdown')
Traceback (most recent call last):
  File "/home/user/wspace/electrum/electrum/util.py", line 1218, in wrapper
    return await func(*args, **kwargs)
  File "/home/user/wspace/electrum/electrum/interface.py", line 649, in wrapper_func
    return await func(self, *args, **kwargs)
  File "/home/user/wspace/electrum/electrum/interface.py", line 675, in run
    await self.open_session(ssl_context=ssl_context)
  File "/home/user/wspace/electrum/electrum/interface.py", line 872, in open_session
    async with _RSClient(
  File "/home/user/.local/lib/python3.10/site-packages/aiorpcx/rawsocket.py", line 167, in __aenter__
    _transport, protocol = await self.create_connection()
  File "/home/user/wspace/electrum/electrum/interface.py", line 285, in create_connection
    return await super().create_connection()
  File "/home/user/.local/lib/python3.10/site-packages/aiorpcx/rawsocket.py", line 163, in create_connection
    return await connector.create_connection(
  File "/usr/lib/python3.10/asyncio/base_events.py", line 1036, in create_connection
    infos = await self._ensure_resolved(
  File "/usr/lib/python3.10/asyncio/base_events.py", line 1418, in _ensure_resolved
    return await loop.getaddrinfo(host, port, family=family, type=type,
  File "/usr/lib/python3.10/asyncio/base_events.py", line 863, in getaddrinfo
    return await self.run_in_executor(
  File "/usr/lib/python3.10/asyncio/base_events.py", line 821, in run_in_executor
    executor.submit(func, *args), loop=self)
  File "/usr/lib/python3.10/concurrent/futures/thread.py", line 169, in submit
    raise RuntimeError('cannot schedule new futures after '
RuntimeError: cannot schedule new futures after interpreter shutdown
```
2025-07-15 12:00:31 +00:00
ThomasV
e442f38d08 Merge pull request #10026 from SomberNight/202507_jsonpatch_monkeypatch_exc
JsonDB: monkeypatch jsonpatch exceptions to avoid leaking secrets
2025-07-15 12:06:39 +02:00
f321x
307181fe5e wizard: add unittests for passphrase flow 2025-07-15 00:19:42 +02:00
f321x
1bb8e2d468 adapt trustedcoin to updated passphrase flow in wizard 2025-07-15 00:19:40 +02:00
f321x
290da21187 qml: change wizard passphrase flow 2025-07-15 00:19:25 +02:00
SomberNight
9e752d2c67 update locale 2025-07-14 21:46:08 +00:00
SomberNight
426e99f5ad update RELEASE-NOTES 2025-07-14 21:40:36 +00:00
SomberNight
f2f1dddcc8 swaps: factor out pubkey_to_rgb_color into core lib 2025-07-14 21:09:18 +00:00
SomberNight
9b5b2bad4a qt: only allow wallet unlock if wallet has ks-enc
fixes https://github.com/spesmilo/electrum/issues/10021
2025-07-14 14:26:14 +00:00
SomberNight
b16760b861 jsonpatch exception-mangling: more robust against secrets in dict keys 2025-07-14 12:53:56 +00:00
SomberNight
195d89a509 JsonDB: monkeypatch jsonpatch exceptions to avoid leaking secrets
closes https://github.com/spesmilo/electrum/issues/10001
2025-07-14 12:16:09 +00:00
f321x
bb5b1b3932 wizard: move pasphrase flow from Qt into Abstract Wizard
moves the separate passphrase flow logic from Qt into the Abstract
Wizard base class so the same flow can be shared between Qt and QML
2025-07-11 16:39:31 +02:00
Sander van Grieken
78a7c85f49 qml: swap: like on desktop, use pkh of nostr pubkey as color 2025-07-11 16:33:40 +02:00
accumulator
62a30c5688 Merge pull request #10017 from f321x/fix_qml_wizard_exception
fix: qml: wizard: delete seed_type from wizard_data if not set explicitly
2025-07-11 11:52:05 +02:00
accumulator
f87243dd5c Merge pull request #10014 from f321x/fix_qml_label_setting
fix: qml: update tx label on detailsChanged signal
2025-07-11 10:50:27 +02:00
SomberNight
0f4d8d6d57 follow-up prev: fix weird pyqt bug re QPushButton.clicked
When clicking the "close_button" button (labelled "Not Now"), the whole app state got bugged:
none of the existing windows (e.g. ElectrumWindow) could get focus anymore.

The signature of PushButton.clicked is:
`void QAbstractButton::clicked(bool checked = false)`
https://doc.qt.io/qt-6/qabstractbutton.html#clicked

and pyqt is probably doing some polymorphism, dynamically deciding if the qt slot wants the "checked" arg or not.

The extremely weird part is that the bug is triggered on clicking "close_button" (probably pyqt is passing "checked" to self.close)
but instead of the current commit, the following diff, touching a completely different button, would also "fix" the issue:

```
diff --git a/electrum/gui/qt/exception_window.py b/electrum/gui/qt/exception_window.py
index eceab89de6..e0162e5827 100644
--- a/electrum/gui/qt/exception_window.py
+++ b/electrum/gui/qt/exception_window.py
@@ -67,7 +67,7 @@ class Exception_Window(BaseCrashReporter, QWidget, MessageBoxMixin, Logger):

         self._report_contents_dlg = None  # type: Optional[ReportContentsDialog]
         collapse_info = QPushButton(_("Show report contents"))
-        collapse_info.clicked.connect(self.show_report_contents_dlg)
+        collapse_info.clicked.connect(lambda: self.show_report_contents_dlg())

         main_box.addWidget(collapse_info)
 ```

No idea why.
2025-07-09 15:32:07 +00:00
f321x
aefb180007 fix: qml: wizard: delete seed_type if not set
deletes the `seed_type` key from `wizard_data` in `WCWalletType` if it
is not explicitly set to prevent a stale value from a previous wizard
flow if the user goes back in the wizard and selects a different wallet
type instead of completing the wizard with the previously selected
wallet type.
This happens as the `apply()` function gets called with the
previously set radio button (e.g. 2fa) if the user goes back, if he then
selects multisig the `2fa_segwit` `seed_type` won't get cleared and
cause the exception later.
Example exception when first selecting 2fa, then going back and creating
a multisig wallet:
```
32.77 | E | gui.qml.qeapp.Exception_Hook | exception caught by crash reporter
Traceback (most recent call last):
  File "/home/vagrant/electrum/electrum/gui/qml/qewizard.py", line 40, in submit
    view = self.resolve_next(self._current.view, wdata)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/vagrant/electrum/electrum/wizard.py", line 78, in resolve_next
    view_accept(wizard_data)
  File "/home/vagrant/electrum/electrum/wizard.py", line 501, in maybe_master_pubkey
    wizard_data['multisig_master_pubkey'] = self.keystore_from_data(wizard_data['wallet_type'], wizard_data).get_master_public_key()
                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/vagrant/electrum/electrum/wizard.py", line 339, in keystore_from_data
    return keystore.from_seed(data['seed'], passphrase=seed_extension, for_multisig=for_multisig)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/vagrant/electrum/electrum/keystore.py", line 1197, in from_seed
    raise BitcoinException('Unexpected seed type {}'.format(repr(t)))
electrum.util.BitcoinException: Unexpected seed type '2fa_segwit'
```

Triggered by the following wizard stack:
```
 30.94 | D | wizard | view=create_seed
 30.94 | D | wizard | resolve_next view is confirm_seed
 30.94 | D | wizard | wizard stack:
0: 0x7fdc6804ae80 - {}

1: 0x7fdc6ac61400 - {'wallet_name': 'wallet_1'}

2: 0x7fdc680d8a80 - {'seed_type': '2fa_segwit', 'wallet_name': 'wallet_1', 'wallet_type': 'multisig'}

3: 0x7fdc6804ab00 - {'multisig_cosigner_data': {}, 'multisig_participants': 2, 'multisig_signatures': 2, 'seed_type': '2fa_segwit', 'wallet_name': 'wallet_1', 'wallet_type': 'multisig'}

4: 0x7fdc6807f0c0 - {'keystore_type': 'createseed', 'multisig_cosigner_data': {}, 'multisig_participants': 2, 'multisig_signatures': 2, 'seed_type': '2fa_segwit', 'wallet_name': 'wallet_1', 'wallet_type': 'multisig'}

c: 0x7fdc6807c380 - {'keystore_type': 'createseed', 'multisig_cosigner_data': {}, 'multisig_participants': 2, 'multisig_signatures': 2, 'seed': '<redacted>', 'seed_extend': False, 'seed_extra_words': '<redacted>', 'seed_type': '2fa_segwit', 'seed_variant': 'electrum', 'wallet_name': 'wallet_1', 'wallet_type': 'multisig'}
 30.94 | W | gui.qml.qeapp | next view: confirm_seed
```
2025-07-09 16:29:20 +02:00