Even though the NIP-47 specification kind of defines that requests should
always pass a params dict in their request i witnessed way too often
that clients don't include it in some requests where it is technically
not neccessary and we fail on it.
Handling this gracefully improves compatibility without obvious
downsides.
Fixes exception in lookup_invoice:
```
62.69 | E | plugins.nwc.nwcserver.NWCServer | Error handling nwc request
Traceback (most recent call last):
File "/home/user/code/vibecoding_vm/electrum/electrum/plugins/nwc/nwcserver.py", line 381, in run_request_task
await task
File "/home/user/code/vibecoding_vm/electrum/electrum/util.py", line 1211, in wrapper
return await func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/code/vibecoding_vm/electrum/electrum/plugins/nwc/nwcserver.py", line 526, in handle_lookup_invoice
info = self.wallet.lnworker.get_payment_info(invoice.payment_hash, direction=RECEIVED)
^^^^^^^^^^^^^^^^^^^^
AttributeError: 'Invoice' object has no attribute 'payment_hash'
```
Always includes bolt11 invoice in response, even if the client already
sent it to us in the request. It doesn't seem useful and is marked
optional in the spec but https://sandbox.albylabs.com considers the
response invalid if the invoice is not included.
Also consider htlcs that are still inflight when calculating
the amount/fee of a payment to prevent calculating an incorrect
payment value/fee when calling get_payment_info directly after
a payment succeeds (one htlc settled, but others could still be
inflight).
This assumes that once a payments htlc got settled all other inflight
htlcs will also get settled.
Include the lightning routing fees into the payment budget accounting
to prevent untrusted nwc clients from exceeding the budget by crafting
a high fee route.
There is now only add_to_budget which will return None if the budget
doesn't allow for this spend. It will always add the spend to the
budget, even if the user has no budget so it can be used for display
purposes (and simpler code, only one path).
NIP-47 got extended to include an optional 'state' field in responses.
Implementing this acutally fixes an issue of Alby Go showing succeeded
payments as failed that appeared recently.
Increase the payment budget before attempting the payment
and decrease again if the payment fails. This prevents a race
where multiple concurrent payments could pass the budget check
before the budget is incremented through any of the other payments.
A lock around the budget is not suitable either as then one long
stuck payment (hold invoice) would render the budget inaccessible for
all other payment attempts.
NIP-47 now defines how client/server should communicate their supported
encryption schemes. For backwards compatibility its not strictly
neccessary to implement this but it seems cleaner to explicitly handle
it.
Call `NWCServer.event_handler_task.cancel()` on asyncio thread.
```
File "/home/user/Documents/electrum/electrum/plugins/nwc/nwcserver.py", line 281, in restart_event_handler
self.event_handler_task.cancel()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
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
```
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.
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.
improve the filtering of incoming requests by checking if they have
explicitly set an expiration tag. If so, they will only be ignored if
this timestamp is exceeded. Otherwise requests older than 30 secons will
get ignored and an error will get sent to the client so the client is
aware it's request arrived too late.
This is done to prevent handling requests the user may already expects
to have failed.
"# don't import net directly, import the module instead (so that net is singleton)"
set_as_network does not work if net is not a singleton, it results in a split worldview.
don't return the spending methods pay_invoice and multi_pay_invoice in
the get_info request and the info event so connections can be used for
services that enforce receive only connections.
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.
- both internal and external plugins require GUI install
(except internal HW plugins, which are 'auto-loaded' and hidden)
- remove init_qt hook
- in Qt, reload wallet windows if plugin enabled/disabled
- add 'uninstall' button to PluginDialog
- add 'add plugins' button to wizard hw screen
- add icons to the plugin list