Commit Graph

9144 Commits

Author SHA1 Message Date
f321x 1b28e6bf73 TxEditor: move swap request to TxEditor
Moves the logic requesting the forward swap into the TxEditor so it can
use the open transport and doesn't have to reconnect to the relays
again.

Also disables the "Preview" button in the TxEditor when the transaction will
send change to lightning.
This should prevent the user from saving the transaction to history and
broadcasting it later or exporting it and broadcasting it through some
external way.
Broadcasting needs to happen directly after the TxEditor so we can send
the second rpc call to the swapserver and await the incoming htlcs
before broadcasting the (funding-) transaction.
2026-01-22 10:09:29 +01:00
f321x a1841600a1 TxEditor: update dynamically based on swap transport
Update the TxEditor (onchain tab) if Send change to lightning is enabled
and the swap transport changes. Connect to swap transport if send change
to lightning gets enabled or if it is enabled and the TxEditor gets
opened.
This allows to nicely show the swap fees without blocking the UI to wait
until the swap manager gets initialized.
2026-01-22 10:09:28 +01:00
f321x 7b828a8317 wallet: check swap provider liquidity for send change to ln
Check the swap providers liquidity as well if we try to send change to
lightning in `make_unsigned_transaction`. It is now expected that the
swap_manager is already initialized when calling
`make_unsigned_transaction`, otherwise no dummy output will get added.
2026-01-22 10:09:26 +01:00
f321x 40811abe45 qt: TxEditor: separate swap transport from tab
Separates the swap transport initialization logic from the submarine
payment tab logic so it can be used for the send change to lightning
functionality too.

Also makes the gui updates on transport establishment more thread safe
by using pyqtSignals instead of calling gui methods from the asyncio
thread.
2026-01-22 10:09:24 +01:00
f321x 844312f5f6 TxEditor: move submarine payment help text to messages
Moves the help text for submarine payments to messages.py
2026-01-22 10:09:22 +01:00
f321x 4f3274e4c6 qt: TxEditor: extend messages for send change to ln
Extend TxEditor.get_messages() with errors for the send change to
lightning function so users see why it wasn't used even though it is
enabled.
2026-01-22 10:09:15 +01:00
ThomasV bf1e0103e4 Merge pull request #10423 from SomberNight/202601_qml_open_passwordless_wallets
qml: fix: allow opening passwordless wallets
2026-01-20 09:29:58 +01:00
ghost43 40b54f5c8b Merge pull request #10170 from SomberNight/202508_debug_mem
add/organise memory_leak helpers
2026-01-19 17:56:27 +00:00
ghost43 4394231d89 Merge pull request #10422 from SomberNight/202601_lnpeer_diag_name
lnpeer: log name of wallet file in each line
2026-01-19 17:11:34 +00:00
SomberNight 6d9ec72853 qml: QEDaemon.setPassword to restore invariant wallets are unlocked
fixes https://github.com/spesmilo/electrum/issues/10415
2026-01-19 17:09:02 +00:00
SomberNight 91f286b422 wallet: minor clean-up and sanity checks for unlock/lock
- unlock() did not handle password=="" well
    instead of the caller converting the arg, as in
    https://github.com/spesmilo/electrum/commit/7113cec4c724c2a443031c71cc8d08fee5e2f385,
    it is more robust for the function itself to do it
- get_unlocked_password() should never return an invalid password
- add is_unlocked()
2026-01-19 17:07:35 +00:00
SomberNight 5b915fbff8 qml: fix: allow opening passwordless wallets
This must be an old regression.
The GUI was not allowing to open a wallet that did not have a password set:
it prompted for a password and did not accept any string (should at least accept empty "").

Without this, it was only possible to open a passwordless wallet if that was the first wallet the user opened
(as otherwise we would overwrite the empty pw with the pw of the current wallet).
2026-01-19 16:41:57 +00:00
SomberNight 0ae10b2a7b lnpeer: log name of wallet file in each line
- if multiple LN-enabled wallets are open, need to know which peer is for which wallet
- note: LNGossip is a singleton
  - if a wallet is named LNGossip, can't distinguish. I think that's ok.

compare log lines:
before:
```
84.82 | I | lnpeer.Peer.[LNWallet, 034cc6216f-f8dcaa6e] | Disconnecting: GracefulDisconnect('Failed to initialize: TimeoutError()')
17.97 | D | lnpeer.Peer.[LNGossip, 0259d4116d-1618547b] | Sending INIT
```
after:
```
 5.80 | D | lnpeer.Peer.[test_segwit_2, 038863cf8a-fd53ef9c] | Sending CHANNEL_READY
 5.92 | D | lnpeer.Peer.[LNGossip, 038863cf8a-6286ffd4] | Received INIT
```
2026-01-19 16:03:42 +00:00
ghost43 ece52b0a4e Merge pull request #10340 from f321x/fingerprint
android: implement biometric authentication (fingerprint)
2026-01-19 15:22:17 +00:00
user 47efb8b108 qml: remove pin code authentication
Completely removes the pin code authentication from qml. The config
option in the wallet preferences has been renamed to "Payment
authentication" and now either asks for the Android system
authentication (Biometric or system pin/password) if enabled or will ask
for the wallet password as fallback.
2026-01-19 14:07:25 +01:00
SomberNight 21248817c9 config: allow setting "forgetconfig" option in file, not just on CLI
- by specifying "default=<VALUE>" in commands.py, <VALUE> would always overwrite what is in the config file
- note `$ ./run_electrum -o setconfig forget_config true` still does not work,
  as that first sets forget_config in memory, and then - by virtue of the setting -
  it refuses to write changes to the config file
  - hence this option would have to be set manually by editing the json
    (or as a CLI flag, as before)

ref https://github.com/spesmilo/electrum/pull/10421#issuecomment-3765862081
2026-01-19 11:30:53 +00:00
user e81ac4b7cd lnpeer: followup #10413
Save the updated htlc set in `Peer._fulfill_htlc_set` and
`Peer._fail_htlc_set()` only after the loop iterated through all htlcs.
This potentially improves performance, especially considering that
writing the db can take >100 ms for larger wallets without partial
writes.
2026-01-16 12:16:23 +01:00
SomberNight a27e2cc6b5 addr_sync: update "stored_height" db field immediately on wallet-open
Repro steps:
- in qt gui, with network enabled, open wallet1
- open wizard, create wallet2 (restore from seed something that has mined history)
- close both wallets, stop electrum
- start electrum with "-o" offline flag, open wallet2
- observe all txs in history tab show up as "unconfirmed"

The cause is that "stored_height" only gets updated ~on new blocks.
So if you created a wallet and closed it soon, its db would not contain "stored_height."
2026-01-15 16:43:22 +00:00
SomberNight c37b844f66 lnutil: change ReceivedMPPStatus.htlcs to frozenset, i.e. immutable
As ThomasV says:

> ReceivedMPPStatus is a Namedtuple, which is immutable, but it contains
> a mutable field. Since ReceivedMPPStatus is not a StoredObject,
> no patch will be created when the htlcs list is modified, and we may
> end up not saving the change to disk if partial writes are enabled.

patch taken from https://github.com/spesmilo/electrum/pull/10395#pullrequestreview-3634244541
closes https://github.com/spesmilo/electrum/pull/10395

Co-authored-by: f321x <f@f321x.com>
2026-01-15 15:59:14 +00:00
ghost43 5266b3d61e Merge pull request #10371 from accumulator/qml_manual_fee_feerate_edit
qml: allow manual editing of fee/feerate
2026-01-15 14:43:08 +00:00
SomberNight 8a3d9fd758 qml: FeePicker: restrict abs/rate editing to mimic wallet.bump_fee/cpfp 2026-01-14 16:53:30 +00:00
SomberNight ca597942fd qml: (trivial) qeinvoice: add type hint 2026-01-14 16:13:00 +00:00
ghost43 5db4a16ace Merge pull request #10410 from f321x/fix_10406
qml: fix invalid QEInvoiceParser state
2026-01-14 15:55:18 +00:00
f321x b599ae7d4a qml: fix invalid QEInvoiceParser state
Fixes the issue described in #10406.
When scanning a lightning invoice we would pass it to
`QEInvoiceParser.fromResolvedPaymentIdentifier()`, however
`fromResolvedPaymentIdentifier()` doesn't reset the state of
`QEInvoiceParser._lnurlData` which is used in QML to evaluate
`payImmediately: invoiceParser.isLnurlPay` in the `onValidationSuccess`
connection.

This change calls `clear()` in `fromResolvedPaymentIdentifier()` to
ensure that `QEInvoiceParser` state gets reset when loading a new invoice.
However when retrieving a bolt11 from a lnurl-pay callback we don't
wan't to reset `QEInvoiceParser._lnurlData` so that `payImmediately` is
true when confirming the lnurl pay dialog, for that I skip calling
`fromResolvedPaymentIdentifier()` and instead call `validateRecipient()`
directly so the `QEInvoiceParser` state doesn't get reset in this case.
2026-01-14 11:42:55 +01:00
f321x 6450187902 qeqrscanner: check requestCode on activity result 2026-01-13 18:10:07 +01:00
user 5dd3dda238 android: implement biometric authentication
Allows to unlock the android app with the android biometric api (e.g.
fingerprint). Can be enabled in the settings.
2026-01-13 18:10:00 +01:00
ThomasV 1845143786 lnpeer: wait_for_received_pending_htlcs_to_get_removed: wait only if peer has been initialized 2026-01-13 17:16:10 +01:00
ThomasV 69af2f77ed Merge pull request #10404 from f321x/fix_10403
ExchangeRate: return NaN if rate is 0
2026-01-13 14:55:20 +01:00
f321x 5199c6c742 ExchangeRate: return NaN if rate is 0
Prevent DivisionByZero exceptions by returning `Decimal('NaN')
instead of `Decimal(0)` if the exchange rate is 0.

Fixes https://github.com/spesmilo/electrum/issues/10403

```
>>> bool(Decimal(0))
False
```
2026-01-13 13:57:14 +01:00
ThomasV 0c29c5006e Merge pull request #10394 from f321x/cosigner_wallet_event_listener
bugfixes: set psbt_nostr event on aio loop and use correct cb in TxEditor
2026-01-13 13:45:06 +01:00
SomberNight 65f245f475 qml: FeePicker: hide "Target" line in "Manual" mode
instead use font colors to hint which textedit is being used for target
2026-01-12 18:42:14 +00:00
SomberNight f387300ab2 qml: FeePicker: use UI_UNIT_NAME constants, instead of hardcoding 2026-01-12 18:41:52 +00:00
ghost43 331ed0fe02 Merge pull request #10400 from f321x/fix_qml_type_error
qml: add close no-op to QEQRScanner to fix type error
2026-01-11 06:06:02 +00:00
f321x 04b7b683ed qml: add close no-op to QEQRScanner to fix type error
Adds close() no-op method to QEQRScanner to prevent type errors like
this:
```
01-02 17:28:09.645 10543 10565 I python  : 162.27 | W | gui.qml.qeapp | file:///data/data/org.electrum.electrum/files/app/electrum/gui/qml/components/SweepDialog.qml:123: TypeError: Property 'close' of object QEQRScanner(0xdd32f66fb600)
is not a function
```
2026-01-08 11:14:06 +01:00
f321x 663fcddc0c AddressSynchronizer: invalidate balance cache on spv
There was a race incorrectly counting transactions with one
confirmations to the unconfirmed balance instead of the confirmed
balance.
This happened because the balance cache of AddressSynchronizer got
invalidated after `on_event_blockchain_updated` and then again after
`receive_history_callback`->`add_transaction`, however when calling
`AddressSynchronizer.get_balance()` before the tx got spv verified the
height would still be counted as 0 (unconfirmed), populating the balance
cache again with the unconfirmed balance.
I noticed this only on QML due to timing differences to Qt.
Invalidating the cache in `AddressSynchronizer.add_verified_tx()` after
the tx got verified causes the balance to get recalculated and shown
correctly.
2026-01-07 18:15:08 +01:00
f321x 72f083d2d0 AddressSynchronizer: remove unneccessary loop
This loop seems like a leftover that is not useful anymore, clearing the
cache once has the same effect.
2026-01-07 16:36:07 +01:00
SomberNight 2172dadf62 hw plugins: coldcard: fix compat with ckcc-protocol v1.5.0
fixes https://github.com/spesmilo/electrum/issues/10386
2026-01-05 16:28:23 +00:00
SomberNight f1f4fc0939 hw plugins: coldcard: log error when I forget to set udev rules
so that next time I check that before changing cables, usb hubs, and VMs o.O
2026-01-05 16:06:24 +00:00
SomberNight c3e373a3b2 lnchannel: chan.lnworker must now always be set, even in unit tests 2026-01-05 15:56:07 +00:00
SomberNight dfeb9918d8 tests: lnchannel: rewrite create_test_channels to use LNWallet 2026-01-05 15:56:04 +00:00
SomberNight 17f41044d5 tests: lnpeer: fix cyclic lnworker.wallet.lnworker inconsistency
These better hold, lol:
wallet.lnworker.wallet == wallet
lnworker.wallet.lnworker == lnworker
2026-01-05 15:56:01 +00:00
SomberNight ea42b02ceb tests: simplify MockLNWallet, add fixme for cyclic inconsistency 2026-01-05 15:55:58 +00:00
SomberNight b292c027c3 lnpeer: move make_local_config to LNWallet
no functional changes
2026-01-05 15:55:41 +00:00
SomberNight 1006e8092f lnworker: split LNWallet and LNWorker: LNWallet "has an" LNWorker
- LNWallet no longer "is-an" LNWorker, instead LNWallet "has-an" LNWorker
- the motivation is to make the unit tests nicer, and allow writing unit tests for more things
  - I hope this makes it possible to e.g. test lnsweep in the unit tests
  - some stuff we would previously have to write a regtest for, maybe we can write a unit test for, now
- in unit tests, MockLNWallet now
  - inherits LNWallet
  - the Wallet is no longer being mocked
2026-01-05 15:55:31 +00:00
f321x 31ac44dddd TxEditor: register correct callback
on_event_channels_updated doesn't get fired if channels change their
state to OPEN. TxEditor needs to use on_event_channel to notice channels
coming online.
2026-01-05 16:36:10 +01:00
f321x e033a5e67b psbt_nostr: add EventListener comment to CosignerWallet
I got confused how on_event_proxy_set can even work if CosignerWallet
doesn't inherit from EventListener until i figured out its children use
the EventListener too. To avoid this confusion i added two comments.
2026-01-05 16:36:05 +01:00
f321x 0d380218a6 bug: psbt_nostr: set CosignerWallet.pending on aio loop
Fixes:
```
Traceback (most recent call last):
  File "/home/user/code/electrum-fork/electrum/plugins/psbt_nostr/qt.py", line 149, in on_receive
    self.mark_pending_event_rcvd(event_id)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
  File "/home/user/code/electrum-fork/electrum/plugins/psbt_nostr/psbt_nostr.py", line 254, in mark_pending_event_rcvd
    self.pending.set()
    ~~~~~~~~~~~~~~~~^^
  File "/usr/lib64/python3.14/asyncio/locks.py", line 192, in set
    fut.set_result(True)
    ~~~~~~~~~~~~~~^^^^^^
  File "/usr/lib64/python3.14/asyncio/base_events.py", line 829, in call_soon
    self._check_thread()
    ~~~~~~~~~~~~~~~~~~^^
  File "/usr/lib64/python3.14/asyncio/base_events.py", line 866, in _check_thread
    raise RuntimeError(
        "Non-thread-safe operation invoked on an event loop other "
        "than the current one")
RuntimeError: Non-thread-safe operation invoked on an event loop other than the current one
```
2026-01-05 16:02:14 +01:00
ghost43 bdcd3f9c7c Merge pull request #10364 from f321x/test_dont_settle_htlcs_forwarding
lnpeer/lnworker: check dont_settle_htlcs when forwarding
2025-12-30 16:13:16 +00:00
ghost43 ca410beae1 Merge pull request #10385 from f321x/bump_zxingcpp
android: bump barcodescannerview, allow tap to focus, rm 16kb patch
2025-12-22 18:27:44 +00:00
ghost43 a50f963a61 Merge pull request #10373 from accumulator/qml_helpdialog_styling
qml: improve HelpDialog styling
2025-12-22 17:29:57 +00:00