9398 Commits

Author SHA1 Message Date
f321x 5d31401211 qml: require authentication for message signing
Requests authentication when trying to sign a message if "Payment
Authentication" is enabled.
2026-01-21 10:21:18 +01:00
f321x 8e5ea8e12d qml: protect Address Private Keys from screenshots
Address private keys weren't protected from screenshots.
2026-01-21 10:21:17 +01:00
f321x aaa314b36e qml: Preferences: disable screenshot protection if !Android
Disables the "Always allow Screenshots" switch in the preferences if the
App isn't running on Android. QML doesn't have screenshot protection
outside of Android so this toggle is misleading.
2026-01-21 10:21:06 +01:00
f321x f8c44886f4 qml: Preferences: add "Security" section
Adds a separate "Security" section to the qml preferences which contains
security related toggles so the Preferences are less mixed up.
2026-01-21 10:19:29 +01:00
SomberNight 15067be527 wallet: stop() to clean-up lnworker even in offline mode 2026-01-20 22:33:24 +00:00
SomberNight 50b10284ac util.CallbackManager: use sets instead of lists
- to gracefully take duplicate calls of register_callbacks(): should be idempotent now
- as a side-effect, the order of the callbacks is changed and not guaranteed
  - not like anyone should have been relying on it before though
2026-01-20 22:33:21 +00:00
SomberNight 5d41891554 utils/memory_leak: factor out count_objects_in_memory
and use weakrefs, to properly allow using this to test garbage collection
2026-01-20 22:33:07 +00:00
SomberNight 3a8fbabcae utils/memory_leak: fix debug_memusage_dump_random_backref_chain 0 case
handle case where no objects of desired type are found
2026-01-20 22:31:08 +00:00
f321x 07f61ebd5a qml: PasswordDialog: show error on invalid password
Currently the PasswordDialog on QML would just close if the user enters
an incorrect password. This is confusing as the user doesn't know why
the dialog closed and if it initiated any action or not.

With the change the PasswordDialog will get the ability to show an error
message and will show "Invalid Password" if an incorrect password is
entered.
I also used it for the password unification warning ("Need to enter
similar password ...") instead of showing a separate popup.
2026-01-20 12:30:31 +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 fb96ba41cd qt: WalletInfoDialog: sort keystores by root fingerprint
Sort the keystore tabs of the WalletInfoDialog by their root
fingerprints. This makes it less confusing when looking at different
wallet instances of the same multisig setup as the tabs will always have
the same order.
2026-01-08 09:53:41 +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