Commit Graph

106 Commits

Author SHA1 Message Date
f321x a1f1b39368 wallet_db: assert WalletDBUpgrader.storage is dict
Assert `WalletDBUpgrader.data` is a regular in-memory dict and not
some StoredDict, so if an exception would happen during a wallet
db upgrade the partial changes don't get commited to disk.
2026-02-20 16:06:42 +01:00
f321x 73a03249df wallet_db: handle non-existing parent_set_key in v65
Handles non-existing parent_set_key in _convert_version_65.

Fixes #10487
2026-02-20 13:58:51 +01:00
ThomasV 5767913e07 lnhtlc: remove unneeded non-initiator fee_update in log 2026-01-28 12:32:59 +01:00
SomberNight 745318d1ec wallet_db: convert_version_66: trivial simplification 2025-12-10 15:53:17 +00:00
f321x 125a921cc4 lnworker: add invoice features to PaymentInfo class
Adds the invoice features to the `PaymentInfo` class so we can check if
the sender respects our requested features (e.g. if they tried to send
mpp if we requested no mpp).
2025-12-10 10:36:31 +01:00
f321x 5be598b808 lnworker: use channel_id instead of scid in ReceivedMPPHtlc
Store the channel id instead of the scid in ReceivedMPPHtlc.
The scid can be None, in theory even for multiple channels at the same
time. Using the channel_id which is always available and unique seems
less error prone at the cost of temporarily higher storage requirements
in the db for the duration of the pending htlcs.

Alternatively we could use the local scid alias however using the
channel_id seems less complex and leaves less room for ambiguity.
2025-12-09 14:44:11 +01:00
f321x 923d48f9db lnworker: differentiate PaymentInfo by direction
Allows storing two different payment info of the same payment hash by
including the direction into the db key.
We create and store PaymentInfo for sending attempts and for requests (receiving),
if we try to pay ourself (e.g. through a channel rebalance) the checks
in `save_payment_info` would prevent this and throw an exception.
By storing the PaymentInfos of outgoing and incoming payments separately in
the db this collision is avoided and it makes it easier to reason about
which PaymentInfo belongs where.
2025-12-01 18:39:56 +01:00
f321x 0f314d1dd9 lnpeer/lnworker: refactor htlc_switch
refactor `htlc_switch` to new architecture to make it more robust
against partial settlement of htlc sets and increase maintainability.
Htlcs are now processed in two steps, first the htlcs are collected into
sets from the channels, and potentially failed on their own already.
Then a second loop iterates over the htlc sets and finalizes only on
whole sets.

# Conflicts:
#	electrum/lnpeer.py
2025-11-27 17:57:14 +01:00
f321x a0455f8382 swaps: allow reverse swaps to external address
Implement logic to claim a reverse swap funding output to any given
address. This allows to do onchain payments to external recipients
through a submarine swap.
2025-11-27 09:50:04 +01:00
ThomasV 7aeef92060 json_db: move json to python conversion logic from
StoredDict to json_db.

convert_key, convert_value are used to convert json objects
to python classes, do not call them in StoredDict.__setitem__

This makes StoredDict agnostic about the type of database we use.
2025-11-07 10:00:10 +01:00
f321x 286fc4b86e lnworker: enforce creation of PaymentInfo for b11
Enforce that the information used to create a bolt11 invoice using
`get_bolt11_invoice()` is similar to the related instance of PaymentInfo
by requiring a PaymentInfo as argument for `get_bolt11_invoice()`.
This way the invoice cannot differ from the created PaymentInfo.
This allows to use the information in PaymentInfo for validation of
incoming htlcs more reliably.

To cover all required information for the creation of a b11 invoice the
PaymentInfo class has to be extended with a expiry and
min_final_cltv_expiry. This requires a db upgrade.
2025-09-30 09:54:35 +02:00
SomberNight b944371ffd adb: change API of util.TxMinedInfo: height() is now always SPV-ed 2025-09-24 13:46:24 +00: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
f321x e80551192b plugins: structure plugin storage in wallet
store all plugin data by plugin name in a root dictionary `plugin_data`
inside the wallet db so that plugin data can get deleted again.
Prunes the data of plugins from the wallet db on wallet stop if the
plugin is not installed anymore.
2025-05-06 13:16:49 +02:00
SomberNight 5c233ac325 ci: enable more flake8 stuff
```
$ export ELECTRUM_LINTERS=E9,E101,E129,E273,E274,E703,E71,E722,F5,F6,F7,F8,W191,W29,B
$ export ELECTRUM_LINTERS_IGNORE=B007,B009,B010,B019,B036,F541,F841
$ flake8 . --count --select="$ELECTRUM_LINTERS" --ignore="$ELECTRUM_LINTERS_IGNORE" --show-source --statistics --exclude "*_pb2.py,electrum/_vendor/"
./electrum/commands.py:98:1: F811 redefinition of unused 'format_satoshis' from line 48
def format_satoshis(x):
^
./electrum/commands.py:437:9: F811 redefinition of unused 'Mnemonic' from line 62
        from .mnemonic import Mnemonic
        ^
./electrum/gui/qt/wizard/wallet.py:37:5: F811 redefinition of unused 'Daemon' from line 14
    from electrum.daemon import Daemon
    ^
./electrum/lntransport.py:14:1: F811 redefinition of unused 'Optional' from line 12
from typing import NamedTuple, List, Tuple, Mapping, Optional, TYPE_CHECKING, Union, Dict, Set, Sequence
^
./electrum/lntransport.py:14:1: F811 redefinition of unused 'TYPE_CHECKING' from line 12
from typing import NamedTuple, List, Tuple, Mapping, Optional, TYPE_CHECKING, Union, Dict, Set, Sequence
^
./electrum/plugin.py:966:13: F811 redefinition of unused 'hid' from line 593
            import hid
            ^
./electrum/plugin.py:1040:13: F811 redefinition of unused 'hid' from line 593
            import hid
            ^
./electrum/util.py:44:1: F811 redefinition of unused 'json' from line 26
import json
^
./electrum/util.py:46:1: F811 redefinition of unused 'NamedTuple' from line 29
from typing import NamedTuple, Optional
^
./electrum/util.py:46:1: F811 redefinition of unused 'Optional' from line 29
from typing import NamedTuple, Optional
^
./electrum/util.py:1456:56: F811 redefinition of unused 'traceback' from line 34
        async def __aexit__(self, exc_type, exc_value, traceback):
                                                       ^
./electrum/wallet_db.py:536:9: F811 redefinition of unused 'LOCAL' from line 46
        LOCAL = 1
        ^
./electrum/wallet_db.py:537:9: F811 redefinition of unused 'REMOTE' from line 46
        REMOTE = -1
        ^
./tests/test_bitcoin.py:28:1: F811 redefinition of unused 'bitcoin' from line 9
from electrum import crypto, constants, bitcoin
^
./tests/test_txbatcher.py:11:1: F811 redefinition of unused 'Transaction' from line 7
from electrum.transaction import Transaction, PartialTxInput, PartialTxOutput, TxOutpoint
^
./tests/test_wallet_vertical.py:20:1: F811 redefinition of unused 'Transaction' from line 10
from electrum.transaction import Transaction, PartialTxOutput, tx_from_any, Sighash
^
16    F811 redefinition of unused 'format_satoshis' from line 48
16

```
2025-04-02 16:21:59 +00:00
SomberNight cba073dfd1 lightning: change derivation of funding_pubkey
Ideally, given an on-chain backup, after the remote force-closes, we should be able to spend our anchor output,
to CPFP the remote commitment tx (assuming the channel used OPTION_ANCHORS).
To spend the anchor output, we need to be able to sign with the local funding_privkey.

Previously we derived the funding_key from the channel_seed (which comes from os.urandom).
Prior to anchors, there was no use case for signing with the funding_key given a channel backup.
Now with anchors, we should make its derivation deterministic somehow, in a way so that it can
be derived given just an on-chain backup.
- one way would be to put some more data into the existing OP_RETURN
  - uses block space
  - the OP_RETURNs can be disabled via "use_recoverable_channels"
  - only the initiator can use OP_RETURNs (so what if channel is in incoming dir?)
- instead, new scheme for our funding_key:
  - we derive the funding_privkey from the lnworker root secret (derived from our bip32 seed)
  - for outgoing channels:
    - lnworker_root_secret + remote_node_id + funding_tx_nlocktime
  - for incoming channels:
    - lnworker_root_secret + remote_node_id + remote_funding_pubkey
  - a check is added to avoid reusing the same key between channels:
      not letting to user open more than one channel with the same peer in a single block
  - only the first 16 bytes of the remote_node_id are used, as the onchain backup OP_RETURNs only contain that
- as the funding_privkey cannot be derived from the channel_seed anymore, it is included in the
imported channel backups, which in turn need a new version defined
  - a wallet db upgrade is used to update already stored imported cbs
  - alternatively we could keep the imported cbs as-is, so no new version, no new funding_privkey field, as it is clearly somewhat redundant given on-chain backups can reconstruct it
    - however adding the field seems easier
      - otherwise the existing code would try to derive the funding_privkey from the channel_seed
      - also note: atm there is no field in the imported backups to distinguish anchor channels vs static-remotekey channels
2025-01-14 17:56:48 +00:00
ThomasV 31884895aa wallet_db: show electrum version in error dialog 2024-05-30 17:19:23 +02:00
SomberNight 2f1095510c bitcoin.py/transaction.py: API changes: rm most hex usage
Instead of some functions operating with hex strings,
and others using bytes, this consolidates most things to use bytes.

This mainly focuses on bitcoin.py and transaction.py,
and then adapts the API usages in other files.

Notably,
- scripts,
- pubkeys,
- signatures
should be bytes in almost all places now.
2024-04-29 17:10:26 +00:00
SomberNight 30c9f5b6b1 walletdb: chan dict: small clean-up (incl db upgrade)
- "fail_htlc_reasons" was removed in 9b1c40e396
- "unfulfilled_htlcs": rm 2 dead items from the 4-tuple,
   and convert False value of forwarding_key
2024-03-01 16:28:46 +00:00
SomberNight 51b7fc04ef walletdb: fix convert_version_58 for partial local txs
The transaction dict can also contain PSBTs (in addition to complete raw hex txs).
This is the case if the user has saved a partial (e.g. unsigned) tx as local into the history.

fixes https://github.com/spesmilo/electrum/issues/8913
2024-02-26 18:54:21 +00:00
SomberNight b3a908f647 WalletDB: (trivial) add type hint 2024-02-12 18:26:08 +00:00
ThomasV 061c821128 Merge pull request #8843 from SomberNight/202401_jsondb_set
walletdb: change structure of prevouts_by_scripthash, and db upgrade
2024-01-31 12:56:11 +01:00
SomberNight cbece71894 walletdb: rm some dead code 2024-01-23 02:20:01 +00:00
SomberNight 79aba5364c walletdb: change structure of prevouts_by_scripthash, and db upgrade
this fixes the test case added in prev
2024-01-23 01:53:06 +00:00
Sander van Grieken 57bd291491 call super().__init__() for WalletFileException descendants,
qt: handle unfinished wallets when opened via File>Open (ref #8809)
2024-01-15 17:06:44 +01:00
SomberNight f7cb523b9d wallet db: deduplicate "seed_type" field
In the db, the 'seed_type' field could be present both at the top-level and inside keystores.

Note:
- both fields had usages
- the top-level field was added in 2.8 re "initial segwit support" (3a64ec0f2e)
  - there was no db upgrade for it, so older files are missing it
  - if present, valid values can be electrum types but also
    other types supported by the wizard, e.g. "bip39"
- the keystore-level field was added in 4.1 (7b7bba2299)
  - there was a db upgrade when it was introduced, so old files also have it
  - if present, valid values can only be electrum types
- there is not much value in the top-level one having a non-electrum value,
  and those values were never used by other parts of the code
  - note that when creating a standard wallet from a bip39 seed, the seed is discarded.
    Only the derived xprv and the derivation path are kept. If we changed this and also kept the seed,
    e.g. to display it to the user, then it would make sense to save the seed type (e.g. "bip39").
    However storing that seed_type would make more sense at the keystore level (not top-level).

We delete the top-level 'seed_type' field.

```
{
    "keystore": {
        "seed_type": "segwit",
        ...
    },
    "seed_type": "segwit",
    ...
}
```
2023-12-01 18:43:37 +00:00
SomberNight 511674a532 contacts: fix adding new contacts
This is a regression from 7ca89f56ee, which introduced StoredList.
The newly added test was failing without the change.

```
Traceback (most recent call last):
  File "/home/user/wspace/electrum/electrum/gui/qt/main_window.py", line 1786, in new_contact_dialog
    self.set_contact(line2.text(), line1.text())
  File "/home/user/wspace/electrum/electrum/gui/qt/main_window.py", line 1435, in set_contact
    self.contacts[address] = ('address', label)
  File "/home/user/wspace/electrum/electrum/contacts.py", line 75, in __setitem__
    self.save()
  File "/home/user/wspace/electrum/electrum/contacts.py", line 62, in save
    self.db.put('contacts', dict(self))
  File "/home/user/wspace/electrum/electrum/json_db.py", line 42, in wrapper
    return func(self, *args, **kwargs)
  File "/home/user/wspace/electrum/electrum/json_db.py", line 318, in put
    self.data[key] = copy.deepcopy(value)
  File "/usr/lib/python3.10/copy.py", line 146, in deepcopy
    y = copier(x, memo)
  File "/usr/lib/python3.10/copy.py", line 231, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "/usr/lib/python3.10/copy.py", line 172, in deepcopy
    y = _reconstruct(x, memo, *rv)
  File "/usr/lib/python3.10/copy.py", line 271, in _reconstruct
    state = deepcopy(state, memo)
  File "/usr/lib/python3.10/copy.py", line 146, in deepcopy
    y = copier(x, memo)
  File "/usr/lib/python3.10/copy.py", line 231, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "/usr/lib/python3.10/copy.py", line 172, in deepcopy
    y = _reconstruct(x, memo, *rv)
  File "/usr/lib/python3.10/copy.py", line 271, in _reconstruct
    state = deepcopy(state, memo)
  File "/usr/lib/python3.10/copy.py", line 146, in deepcopy
    y = copier(x, memo)
  File "/usr/lib/python3.10/copy.py", line 231, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "/usr/lib/python3.10/copy.py", line 161, in deepcopy
    rv = reductor(4)
TypeError: cannot pickle '_thread.RLock' object
```
2023-11-01 17:28:29 +00:00
ThomasV 98a4d7b60d public channels:
- send node and channel announcements.
 - store channel_flags in constraints
 - store signatures in local and remote configs
2023-10-16 13:54:16 +02:00
Sander van Grieken 7ca9b735d5 daemon: refactor load_wallet to not just return None, but raise specific exceptions.
The following exceptions should be expected:
FileNotFoundError: given wallet path does not exist
StorageReadWriteError: given file is not readable/writable or containing folder is not writable
InvalidPassword: wallet requires a password but no password or an invalid password was given
WalletFileException: any internal wallet data issue. specific subclasses can be caught separately:
-  WalletRequiresSplit: wallet needs splitting (split_data passed in Exception)
-  WalletRequiresUpgrade: wallet needs upgrade, and no upgrade=True was passed to load_wallet
-  WalletUnfinished: wallet file contains an action and needs additional information to finalize. (WalletDB passed in exception)

Removed qml/qewalletdb.py

This patch also fixes load_wallet calls in electrum/scripts and adds a qml workaround for dialogs opening and closing so
fast that the dialog opened==true property change is missed (which we need to manage the dialog/page stack)
2023-10-10 17:42:07 +02:00
ThomasV 019be008d6 json_db: do not overload load_data.
instead, pass an upgrader function to the constructor.
2023-09-24 12:10:35 +02:00
ThomasV 30038f4ace follow-up previous commit: write storage only if there was an upgrade 2023-09-23 15:15:17 +02:00
ThomasV 56e80c20d7 wallet_db upgrade: do not use '/' in StoredDict keys 2023-09-23 11:05:36 +02:00
SomberNight a7128438d4 wallet_db: fix typo in renamed arg "upgrade"
follow-up 8be3c4dadd
2023-09-22 16:36:47 +00:00
ThomasV 68159b3ef6 walletDB: replace 'manual_upgrades' parameter with 'upgrade', with opposite semantics 2023-09-22 15:02:07 +02:00
ThomasV b5bc5ff9ed Separate WalletDB from storage upgrades.
Make sure that WalletDB.data is always a StoredDict.
Perform db upgrades in a separate class, since they
operate on a dict object.
2023-09-22 15:02:06 +02:00
SomberNight 4e6e6f76ca invoices: also run amount-validator on setter
- @amount_msat.validator prevents the creation of invoices with e.g. too large amounts
- however the qml gui is mutating invoices by directly setting the `amount_msat` field,
  and it looks like attrs validators only run during init.
  We can use `on_setattr` (introduced in attrs==20.1.0).
- a wallet db upgrade is added to rm existing insane invoices
- btw the qml gui was already doing its own input validation on the textedit
  (see qeconfig.btcAmountRegex). however that only limits the input to not have more
  chars than what is needed to represent 21M BTC (e.g. you can still enter 99M BTC,
  which the invoice logic does not tolerate later on - but is normally caught).

fixes https://github.com/spesmilo/electrum/issues/8582
2023-08-22 18:10:21 +00:00
ThomasV b96cc82333 Make storage a field of db
This comes from the jsonpatch_new branch.
I rather have in master now, because it touches a lot of filese.
2023-08-18 08:08:31 +02:00
SomberNight cee22abcb5 wallet_db: upgrade to version 53, for imported chan backups
follow-up https://github.com/spesmilo/electrum/pull/8536

This replaces 69336befee, which was insufficient.
#8536 added a new field into the struct, which older versions do not ignore but raise:
opening a wallet file with new code updated the struct to include it,
after which old code could no longer open the wallet file.
i.e. #8536 was an invisible wallet upgrade, breaking compat.
This commit simply formalises the wallet upgrade: old code will now show
an understandable error when trying to open new files.
2023-08-17 14:08:27 +00:00
ThomasV b8b36c7c30 follow-up prev: fix flake8 test 2023-06-24 12:56:55 +02:00
ThomasV 411098f293 move methods from wallet_db to json_db
the goal of this commit is to call JsonDB.__init__ with data,
not an empty dict
2023-06-24 12:45:07 +02:00
ThomasV 759eaf1cf5 json_db: register extra types outside of constructor 2023-06-23 12:16:14 +02:00
ThomasV 295734fc53 storage: encapsulate type conversions of stored objects using
decorators (instead of overloading JsonDB._convert_dict and
 _convert_value)
 - stored_in for elements of a StoreDict
 - stored_as for singletons
 - extra register methods are defined for key conversions

This commit was adapted from the jsonpatch branch
2023-06-18 13:08:57 +02:00
SomberNight 68fb996d20 wallet_db version 52: break non-homogeneous multisig wallets
- case 1: in version 4.4.1, 4.4.2, the qml GUI wizard allowed creating multisig wallets with an old_mpk as cosigner.
- case 2: in version 4.4.0, 4.4.1, 4.4.2, the qml GUI wizard allowed creating multisig wallets with mixed xpub/Ypub/Zpub.

The corresponding missing input validation was a bug in the wizard, it was unintended behaviour. Validation was added in d2cf21fc2b. Note however that there might be users who created such wallet files.

Re case 1 wallet files: there is no version of Electrum that allows spending from such a wallet. Coins received at addresses are not burned, however it is technically challenging to spend them. (unless the multisig can spend without needing the old_mpk cosigner in the quorum).

Re case 2 wallet files: it is possible to create a corresponding spending wallet for such a multisig, however it is a bit tricky. The script type for the addresses in such a heterogeneous xpub wallet is based on the xpub_type of the first keystore. So e.g. given a wallet file [Yprv1, Zpub2] it will have sh(wsh()) scripts, and the cosigner should create a wallet file [Ypub1, Zprv2] (same order).

Technically case 2 wallet files could be "fixed" automatically by converting the xpub types as part of a wallet_db upgrade. However if the wallet files also contain seeds, those cannot be converted ("standard" vs "segwit" electrum seed).
Case 1 wallet files are not possible to "fix" automatically as the cosigner using the old_mpk is not bip32 based.

It is unclear if there are *any* users out there affected by this. I suspect for case 1 it is very likely there are none (not many people have pre-2.0 electrum seeds which were never supported as part of a multisig who would also now try to create a multisig using them); for case 2 however there might be.

This commit breaks both case 1 and case 2 wallets: these wallet files can no longer be opened in new Electrum, an error message is shown and the crash reporter opens. If any potential users opt to send crash reports, at least we will know they exist and can help them recover.
2023-05-11 14:26:11 +00:00
SomberNight 312f2641e7 don't use bare except
use "except Exception", or if really needed explicitly "except BaseException"
2023-04-24 12:58:01 +00:00
SomberNight 950065a3de Store file creation date and version in db
Store the electrum version used to create a wallet file and a timestamp,
in the file itself. This can be useful for debugging.
2023-03-13 15:15:50 +00:00
ThomasV 719b468eee Refresh bolt11 routing hints when channel liquidity changes:
- wallet_db update: separate Invoices and Requests.
 - do not store bolt11 invoice in Request
2023-02-28 15:33:17 +01:00
ThomasV 015648c4e5 Move get_dict method from wallet_db to json_db.
Define overloaded methods: _convert_dict _convert_values
2022-11-03 12:41:49 +01:00
ThomasV 14e96f4d53 Index request by ID instead of receiving address.
Replace get_key_for_outgoing_invoice, get_key_for_incoming_request
with Invoice.get_id()

When a new request is created, reuse addresses of expired requests (fixes #7927)

The API is changed for the following commands:
 get_request, get_invoice,
 list_requests, list_invoices,
 delete_request, delete_invoice
2022-09-02 10:58:11 +02:00
SomberNight 7b8e257ebb wallet db upgrade: rm support of "legacy" lightning channels
("legacy" as in pre-static-remote-key channels)
2022-08-16 08:49:17 +02:00
SomberNight ed65f335bd wallet_db upgrade: fix possible corruption of invoice amounts
see https://github.com/spesmilo/electrum/pull/7774
2022-07-15 18:26:13 +02:00