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
see https://github.com/spesmilo/electrum/issues/5376
crash report for electrum 4.2.1:
```
Traceback (most recent call last):
File "/home/user/wspace/electrum/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/Electrum/kivy/base.py", line 347, in mainloop
File "/home/user/wspace/electrum/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/Electrum/kivy/base.py", line 391, in idle
File "/home/user/wspace/electrum/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/Electrum/kivy/base.py", line 342, in dispatch_input
File "/home/user/wspace/electrum/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/Electrum/kivy/base.py", line 308, in post_dispatch_input
File "kivy/_event.pyx", line 724, in kivy._event.EventDispatcher.dispatch
File "/home/user/wspace/electrum/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/Electrum/kivy/uix/behaviors/button.py", line 179, in on_touch_up
File "kivy/_event.pyx", line 720, in kivy._event.EventDispatcher.dispatch
File "kivy/_event.pyx", line 1263, in kivy._event.EventObservers.dispatch
File "kivy/_event.pyx", line 1147, in kivy._event.EventObservers._dispatch
File "/home/user/wspace/electrum/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/Electrum/kivy/lang/builder.py", line 57, in custom_callback
File "<string>", line 39, in <module>
File "/home/user/wspace/electrum/.buildozer/android/app/electrum/gui/kivy/uix/dialogs/settings.py", line 173, in cb
File "kivy/properties.pyx", line 498, in kivy.properties.Property.__set__
File "kivy/properties.pyx", line 545, in kivy.properties.Property.set
File "kivy/properties.pyx", line 600, in kivy.properties.Property.dispatch
File "kivy/_event.pyx", line 1263, in kivy._event.EventObservers.dispatch
File "kivy/_event.pyx", line 1169, in kivy._event.EventObservers._dispatch
File "/home/user/wspace/electrum/.buildozer/android/app/electrum/gui/kivy/main_window.py", line 206, in on_use_gossip
File "/home/user/wspace/electrum/.buildozer/android/app/electrum/network.py", line 368, in start_gossip
File "/home/user/wspace/electrum/.buildozer/android/app/electrum/channel_db.py", line 308, in __init__
File "/home/user/wspace/electrum/.buildozer/android/app/electrum/sql_db.py", line 30, in __init__
File "/home/user/wspace/electrum/.buildozer/android/platform/build-armeabi-v7a/build/other_builds/python3/armeabi-v7a__ndk_target_21/python3/Lib/asyncio/locks.py", line 260, in __init__
File "/home/user/wspace/electrum/.buildozer/android/platform/build-armeabi-v7a/build/other_builds/python3/armeabi-v7a__ndk_target_21/python3/Lib/asyncio/events.py", line 639, in get_event_loop
RuntimeError: There is no current event loop in thread 'GUI'.
```
similar to 05fd424548
from logs when running tests:
--- Logging error ---
Traceback (most recent call last):
File "...\Python39\lib\logging\__init__.py", line 1082, in emit
stream.write(msg + self.terminator)
ValueError: I/O operation on closed file.
Call stack:
File "...\Python39\lib\threading.py", line 912, in _bootstrap
self._bootstrap_inner()
File "...\Python39\lib\threading.py", line 954, in _bootstrap_inner
self.run()
File "...\Python39\lib\threading.py", line 892, in run
self._target(*self._args, **self._kwargs)
File "...\electrum\electrum\sql_db.py", line 71, in run_sql
self.logger.info("SQL thread terminated")
Message: 'SQL thread terminated'
Arguments: ()
- trampoline is enabled by default in config, to prevent download of `gossip_db`.
(if disabled, `gossip_db` will be downloaded, regardless of the existence of channels)
- if trampoline is enabled:
- the wallet can only open channels with trampoline nodes
- already-existing channels with non-trampoline nodes are frozen for sending.
- there are two types of trampoline payments: legacy and end-to-end (e2e).
- we decide to perform legacy or e2e based on the invoice:
- we use trampoline_routing_opt in features to detect Eclair and Phoenix invoices
- we use trampoline_routing_hints to detect Electrum invoices
- when trying a legacy payment, we add a second trampoline to the path to preserve privacy.
(we fall back to a single trampoline if the payment fails for all trampolines)
- the trampoline list is hardcoded, it will remain so until `trampoline_routing_opt` feature flag is in INIT.
- there are currently only two nodes in the hardcoded list, it would be nice to have more.
- similar to Phoenix, we find the fee/cltv by trial-and-error.
- if there is a second trampoline in the path, we use the same fee for both.
- the final spec should add fee info in error messages, so we will be able to fine-tune fees
exceptions below are raised when running python3 with "-X dev":
Traceback (most recent call last):
File "...\electrum\electrum\util.py", line 999, in run_with_except_hook
run_original(*args2, **kwargs2)
File "...\Python38\lib\threading.py", line 870, in run
self._target(*self._args, **self._kwargs)
File "...\electrum\electrum\sql_db.py", line 55, in run_sql
future.set_result(result)
File "...\Python38\lib\asyncio\base_events.py", line 721, in call_soon
self._check_thread()
File "...\Python38\lib\asyncio\base_events.py", line 758, in _check_thread
raise RuntimeError(
RuntimeError: Non-thread-safe operation invoked on an event loop other than the current one
Traceback (most recent call last):
File "...\electrum\electrum\gui\qt\main_window.py", line 3009, in closeEvent
self.clean_up() #
File "...\electrum\electrum\gui\qt\main_window.py", line 3026, in clean_up
self.gui_object.close_window(self)
File "...\electrum\electrum\gui\qt\__init__.py", line 340, in close_window
self.daemon.stop_wallet(window.wallet.storage.path)
File "...\electrum\electrum\daemon.py", line 518, in stop_wallet
wallet.stop()
File "...\electrum\electrum\wallet.py", line 344, in stop
self.lnworker.stop()
File "...\electrum\electrum\lnworker.py", line 602, in stop
super().stop()
File "...\electrum\electrum\lnworker.py", line 273, in stop
self.listen_server.close()
File "...\Python38\lib\asyncio\base_events.py", line 337, in close
self._loop._stop_serving(sock)
File "...\Python38\lib\asyncio\proactor_events.py", line 849, in _stop_serving
future.cancel()
File "...\Python38\lib\asyncio\windows_events.py", line 80, in cancel
return super().cancel()
File "...\Python38\lib\asyncio\base_events.py", line 721, in call_soon
self._check_thread()
File "...\Python38\lib\asyncio\base_events.py", line 758, in _check_thread
raise RuntimeError(
RuntimeError: Non-thread-safe operation invoked on an event loop other than the current one