asyncio.get_event_loop() became deprecated in python3.10. (see https://github.com/python/cpython/issues/83710)
```
.../electrum/electrum/daemon.py:470: DeprecationWarning: There is no current event loop
self.asyncio_loop = asyncio.get_event_loop()
.../electrum/electrum/network.py:276: DeprecationWarning: There is no current event loop
self.asyncio_loop = asyncio.get_event_loop()
```
Also, according to that thread, "set_event_loop() [... is] not deprecated by oversight".
So, we stop using get_event_loop() and set_event_loop() in our own code.
Note that libraries we use (such as the stdlib for python <3.10), might call get_event_loop,
which then relies on us having called set_event_loop e.g. for the GUI thread. To work around
this, a custom event loop policy providing a get_event_loop implementation is used.
Previously, we have been using a single asyncio event loop, created with
util.create_and_start_event_loop, and code in many places got a reference to this loop
using asyncio.get_event_loop().
Now, we still use a single asyncio event loop, but it is now stored as a global in
util._asyncio_event_loop (access with util.get_asyncio_loop()).
I believe these changes also fix https://github.com/spesmilo/electrum/issues/5376
The call to super was removed in 4efcb53d24 ,
so that LNWallet's taskgroup would not run _maintain_connectivity, AFAICT.
The way it was done meant that "main_loop" itself would not run for LNWallet, only for LNGossip - very confusing.
It is only due to a quirk in the behaviour of TaskGroups that the group "started" at all.
```
Traceback (most recent call last):
File "...\electrum\electrum\gui\qt\main_window.py", line 898, in timer_actions
self.update_wallet()
File "...\electrum\electrum\gui\qt\main_window.py", line 1040, in update_wallet
self.update_tabs()
File "...\electrum\electrum\gui\qt\main_window.py", line 1047, in update_tabs
self.history_model.refresh('update_tabs')
File "...\electrum\electrum\util.py", line 439, in <lambda>
return lambda *args, **kw_args: do_profile(args, kw_args)
File "...\electrum\electrum\util.py", line 435, in do_profile
o = func(*args, **kw_args)
File "...\electrum\electrum\gui\qt\history_list.py", line 275, in refresh
transactions = wallet.get_full_history(
File "...\electrum\electrum\util.py", line 439, in <lambda>
return lambda *args, **kw_args: do_profile(args, kw_args)
File "...\electrum\electrum\util.py", line 435, in do_profile
o = func(*args, **kw_args)
File "...\electrum\electrum\wallet.py", line 947, in get_full_history
lnworker_history = self.lnworker.get_onchain_history() if self.lnworker and include_lightning else {}
File "...\electrum\electrum\lnworker.py", line 911, in get_onchain_history
tx_height = self.lnwatcher.get_tx_height(swap.funding_txid)
AttributeError: 'NoneType' object has no attribute 'get_tx_height'
```
fixes https://github.com/spesmilo/electrum/issues/7750
Each task we schedule on `TaskThread` can provide an optional `cancel` method.
When stopping `TaskThread`, we call this `cancel` method on all tasks in the queue.
If the currently running task does not implement `cancel`, `TaskThread.stop` will block
until that task finishes.
Note that there is a significant change in behaviour here:
`ElectrumWindow.run_coroutine_from_thread` and `ElectrumWindow.pay_lightning_invoice`
previously serialised the execution of their coroutines via wallet.thread.
This is no longer the case: they will now schedule coroutines immediately.
So for example, the GUI now allows trying to pay multiple LN invoices "concurrently".
Messages are sent in reestablish_channel (async)
Message checks and force_close are performed in on_channel_reestablish (not async).
That task should not be cancelled if the connection is closed.
Revert 57583c05cf
It is not safe to keep using the channel after we attempted to broadcast a force-close,
even if the broadcast errored: the server cannot be trusted wrt to errors.
Note that if there is a network-error, due to the state-transition, the GUI won't offer
the force-close option to the user again. However, LNWallet will periodically rebroadcast
the tx automatically (in on_channel_update); and we also save the tx as local into the
wallet.
* Refactor `create_trampoline_route`.
* Enables end-to-end multi-trampoline multipart payments.
Trampoline-to-legacy payments are still not enabled, as this is
currently not supported by Eclair.
* Reverts to a global trampoline fee level, as trampoline failures
are currently not handled properly, see (#7648), which doubles
fee rates.
aiorpcx 0.20 changed the behaviour/API of TaskGroups.
When used as a context manager, TaskGroups no longer propagate
exceptions raised by their tasks. Instead, the calling code has
to explicitly check the results of tasks and decide whether to re-raise
any exceptions.
This is a significant change, and so this commit introduces "OldTaskGroup",
which should behave as the TaskGroup class of old aiorpcx. All existing
usages of TaskGroup are replaced with OldTaskGroup.
closes https://github.com/spesmilo/electrum/issues/7446
I think this was originally needed due to incorrect management of group lifecycles,
which our current code is doing better.
also note that if we needed this, in newer aiorpcx, the name of
the field was ~changed from `_closed` to `joined`:
239002689a
- Separates the trampoline and local routing multi-part payment cases.
- Ask only for splits that don't send over a single channel (those have
been tried already in the single-part case).
- Makes sure that create_routes_for_payment only yields partial routes
that belong to a single split configuration.
- Tracks trampoline fee levels on a per node basis, previously, in the
case of having two channels with a trampoline forwarder, the global
fee level would have increased by two levels upon first try.
- The splitting algorithm is redesigned to use random distribution of
subsplittings over channels.
- Splittings can include multiple subamounts within a channel.
- The single-channel splittings are implicitly activated once the
liquidity hints don't support payments of large size.
Fixes: after adding a payment request, if the process was killed,
the payreq might get lost. In case of using the GUI, neither the
callee nor the caller called wallet.save_db().
Unclear where wallet.save_db() should be called...
Now each method tries to persist their changes by default,
but as an optimisation, the caller can pass write_to_disk=False
e.g. when calling multiple such methods and then call wallet.save_db() itself.
If we had partial writes, which would either rm the need for wallet.save_db()
or at least make it cheaper, this code might get simpler...
related: https://github.com/spesmilo/electrum/pull/6435
related: https://github.com/spesmilo/electrum/issues/4823
see code comment.
While the balance in the channels might shift before the sender tries to
pay the invoice, as we are not a forwarding node, that seems unlikely to matter.
- better error handling: previously we stopped all attempts on any of
TRAMPOLINE_EXPIRY_TOO_SOON, UNKNOWN_NEXT_PEER, TEMPORARY_NODE_FAILURE.
Instead we should retry (but see code comments).
- previously payments failed if ALL of the following criteria applied:
- sender is paying via trampoline, but not via the ACINQ node (which is
special cased)
- receiver only has private channels and has put r_tags into invoice, along
with setting the trampoline feature bit in the invoice, however the receiver
is not connected to any trampoline forwarders directly
The sender would then assume that the private routing hints in the invoice
correspond to trampoline forwarders.
- also, previously if both the sender and the recipient used trampoline and
they shared a trampoline forwarder (that they were both connected to), the
private channels the recipient had (with nodes other than the shared TF)
would never be attempted.
Adds liquidity hints for the sending capabilities of routing channels in the
graph. The channel blacklist is incorporated into liquidity hints.
Liquidity hints are updated when a payment fails with a temporary
channel failure or when it succeeds. Liquidity hints are used to give a
penalty in the _edge_cost heuristics used by the pathfinding algorithm.
The base penalty in (_edge_cost) is removed because it is now part of the
liquidity penalty. We don't return early from get_distances, as we want
to explore all channels.