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.
The toolbar button is very small and some users probably don't even
notice it. As we have lots of space in the toolbar anyways i think it
makes sense to add some text to it to make it more visible and easier to
click. Maybe this is also useful for screen readers.
Launch `WatchtowerPlugin.watchtower.start_watching()` through
asyncio.run_coroutine_threadsafe instead of ensure_future to prevent the
following exception from happening when running python with
`PYTHONASYNCIODEBUG=1`.
```
20251103T165007.087746Z | ERROR | plugin.Plugins | cannot initialize plugin watchtower: Error loading watchtower plugin: RuntimeError('Non-thread-safe operation invoked on an event loop other than the current one')
Traceback (most recent call last):
File "/home/user/code/electrum-fork/electrum/plugin.py", line 629, in load_plugin_by_name
plugin = module.Plugin(self, self.config, name)
File "/home/user/code/electrum-fork/electrum/plugins/watchtower/watchtower.py", line 59, in __init__
asyncio.ensure_future(self.watchtower.start_watching())
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib64/python3.14/asyncio/tasks.py", line 732, in ensure_future
return loop.create_task(coro_or_future)
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
File "/usr/lib64/python3.14/asyncio/base_events.py", line 468, in create_task
return self._task_factory(self, coro, **kwargs)
~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/code/electrum-fork/electrum/util.py", line 1773, in factory
task = asyncio.Task(coro, loop=loop_, **kwargs)
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
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/home/user/code/electrum-fork/electrum/plugin.py", line 184, in load_plugins
self.load_plugin_by_name(name)
~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
File "/home/user/code/electrum-fork/electrum/plugin.py", line 631, in load_plugin_by_name
raise Exception(f"Error loading {name} plugin: {repr(e)}") from e
Exception: Error loading watchtower plugin: RuntimeError('Non-thread-safe operation invoked on an event loop other than the current one')
```
This is an optimisation and possible hardening against traffic analysis.
After a new block is mined, we sometimes receive "blockchain.scripthash.subscribe" notifications. If so, this is often due to the just mined block including mempool txs we already knew about.
Normally we would call the "blockchain.scripthash.get_history" RPC, to get full history of the affected scripthash.
Instead now we first optimistically guess that all mempool txs touching this scripthash just got mined and see if by assuming that we can reproduce the announced sh status.
- if yes, we saved a network RTT by not having to call "blockchain.scripthash.get_history"
- if no, we request the history from the server using the RPC
Some regtest tests depend on manual fee injection to simulate certain
mempool conditions (e.g. lnwatcher_waits_until_fees_go_down). This is
done by manually injecting fee estimates into the `Network` object using
the `test_inject_fee_etas` cli command. However it can still happen that
the Network automatically updates its fee estimates from the connected
electrum server in the time between injecting the fee and the actual
tested logic making decisions based on the fee. This causes the test to
fail sometimes.
By setting the `test_disable_automatic_fee_eta_update` true the Network
will stop automatically updating the fee estimates and the test will
behave as expected.
Stops the running regtest if one test fails (using the --failfast
option) and makes the wallet data directories of alice, bob and carol
available for debugging. This seems helpful to fix issues only happening
on the CI.
The amount edit box is not turning red anymore if the amount is higher
than the wallet balance, so this string can be removed.
Alternatively it could be made red again but seems like nobody missed
it.
Allow to open the BalanceDialog, by clicking on the Pie Diagram in the
bottom left corner of the main window, if there is an active warning,
even if the wallets balance is 0. Right now the user can see the
warning icon for the lighting channel reserve, but cannot click on the
icon to read the actual warning if there is no balance in the wallet.
peer initialization was never awaited in the `add_peer` method.
This awaits the initialization of the peer so that the caller
actually knows if connection succeeded or timed out.
Because `LNPeer.initialized` was awaited in
`LNPeer._query_gossip()` instead of the main loop the other tasks got
spawned concurrently and each task on its own has to wait for the
initialization. In `LNPeer._send_own_gossip()` this was missing, instead
there is a fixed 10 sec sleep. If the connection was not initialized but
the 10 sec are exceeded `_send_own_gossip()` tries to send gossip and
causes this exception as the `LNTransport` is not ready:
```
2.13 | E | lnpeer.Peer.[LNWallet, 0288fa27c0-bc1900c8] | Exception in main_loop: AttributeError("'LNTransport' object has no attribute 'sk'")
Traceback (most recent call last):
File "/home/user/code/electrum-fork/electrum/util.py", line 1232, in wrapper
return await func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/code/electrum-fork/electrum/lnpeer.py", line 511, in wrapper_func
return await func(self, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/code/electrum-fork/electrum/lnpeer.py", line 525, in main_loop
async with self.taskgroup as group:
^^^^^^^^^^^^^^
File "/home/user/code/electrum-fork/env/lib/python3.14/site-packages/aiorpcx/curio.py", line 304, in __aexit__
await self.join()
File "/home/user/code/electrum-fork/electrum/util.py", line 1420, in join
task.result()
~~~~~~~~~~~^^
File "/home/user/code/electrum-fork/electrum/lnpeer.py", line 573, in _send_own_gossip
self.send_node_announcement(alias, color)
~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
File "/home/user/code/electrum-fork/electrum/lnpeer.py", line 1830, in send_node_announcement
self.transport.send_bytes(raw_msg)
~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
File "/home/user/code/electrum-fork/electrum/lntransport.py", line 225, in send_bytes
lc = aead_encrypt(self.sk, self.sn(), b'', l)
^^^^^^^
AttributeError: 'LNTransport' object has no attribute 'sk'. Did you mean: 'sn'?
```
By awaiting the initialization directly in the `main_loop` it is more
clear that the task getting spawned subsequently depend on the transport
being available and separates the initialization more clearly these
other functions.
The string is not translated and might not be accessible for non-english
speakers, even though its relatively self-explanatory i think adding
this emoji makes it more accessible.
The ServerWidget was not working properly, when switching from "Manual
Mode" to "Auto Connect" the change wouldn't get saved as it depended on
having a correct server string entered (which isn't neccessary for Auto
Connect).
Also makes the widget behave more sane by cleaning the server input if
Auto Connect is enabled and switching to Manual Mode if the user
manually selects a server.
Update the ServerWidget every time it is shown (on initialization and
also when the user opens it again or switches between network dialog
tabs).
This will clean it up if the user has entered some invalid server and
closes it, otherwise this server would stay in the input field until the
application is restarted.
The list of servers in the ServerWidget allows the user to right click
and 'Use as server' on the servers in the list, however internally it
was handled differently than what the user would expect when clicking on
'Use as server'. E.g. if the user selects a server in autoconnect mode
it would still stay in autoconnect mode so the server could switch again
to another server any time? Now it will also change the mode to manual
(or stay in single server mode if that was selected before), making it
clear that this server will stay selected.
If the user clicks on "Follow this branch" the connect mode will get changed to
autoconnect as internally we connect to a random interface on this
branch.
Adds a 1024 (unpadded) byte budget to the PaddedRSTransport below which
messages are instantly flushed down the socket before the transport will
beginn waiting for the buffer to reach MIN_PACKET_SIZE (1024).
This allows to get the first couple of messages sent quickly when
starting the wallet to improve UX.
Prevents the client from accidentally connecting to a server on a
different network.
I noticed its possible to connect to mainnet servers on a signet
instance causing the recent peers to get populated with mainnet peers
rendering the wallet instance barely usable. Doing this check should
prevent this and similar issues.