i noticed that the `show_bitcoin_paper()` call can be a bit slow on some
machines blocking the gui for multiple seconds without giving feedback.
Wrapping it in a waiting dialog gives the user some feedback that
something is happening.
get_utxos() is called pretty often, both spuriously,
and on focus change, on tab switch, &c.
It blocks as it iterates, functionally, /every/ address the wallet knows of.
On large wallets (like testnet
vpub5VfkVzoT7qgd5gUKjxgGE2oMJU4zKSktusfLx2NaQCTfSeeSY3S723qXKUZZaJzaF6YaF8nwQgbMTWx54Ugkf4NZvSxdzicENHoLJh96EKg
from #6625 with 11k TXes and 10.5k addresses),
this takes 1.3s of 100%ed CPU usage,
basically in a loop from the UI thread.
get_utxos() is 50-70% of the flame-graph when sampling
a synced wallet process.
This data is a function of the block-chain state,
and we have hooks that notify us of when the block-chain state changes:
we can just cache the result and only re-compute it then.
For example, here's a trace log where get_utxos() has
print(end - start, len(domain), block_height)
and a transaction is clearing:
1.3775344607420266 10540 4507192
0.0010390589013695717 10540 4507192 cached!
0.001393263228237629 10540 4507192 cached!
0.0009001069702208042 10540 4507192 cached!
0.0010241391137242317 10540 4507192 cached!
...
0.00207632128149271 10540 4507192 cached!
0.001397700048983097 10540 4507192 cached!
invalidate_cache
1.4686454269103706 10540 4507192
0.0012429207563400269 10540 4507192 cached!
0.0015075239352881908 10540 4507192 cached!
0.0010459059849381447 10540 4507192 cached!
0.0009669591672718525 10540 4507192 cached!
...
on_event_blockchain_updated
invalidate_cache
1.3897203942760825 10540 4507193
0.0010689008049666882 10540 4507193 cached!
0.0010420521721243858 10540 4507193 cached!
...
invalidate_cache
1.408584670163691 10540 4507193
0.001336586195975542 10540 4507193 cached!
0.0009196233004331589 10540 4507193 cached!
0.0009176661260426044 10540 4507193 cached!
...
about 30s of low activity.
Without this patch, the UI is prone to freezing,
running behind, and I wouldn't be surprised if UI thread blocking
on Windows ends up crashing to some extent as the issue notes.
In the log, this manifests as a much slower but consistent
stream of full 1.3-1.4s updates during use,
and every time the window is focused.
Consider e.g.:
```
class AddressSynchronizer(Logger, EventListener):
[... snip ...]
@event_listener
@with_lock
def on_event_blockchain_updated(self, *args):
self._get_balance_cache = {} # invalidate cache
self.db.put('stored_height', self.get_local_height())
```
was raising:
```
func.__qualname__='with_lock.<locals>.func_wrapper'
Traceback (most recent call last):
File "...\electrum\run_electrum", line 105, in <module>
from electrum.logging import get_logger, configure_logging # import logging submodule first
File "...\electrum\electrum\__init__.py", line 19, in <module>
from .wallet import Wallet
File "...\electrum\electrum\wallet.py", line 70, in <module>
from .address_synchronizer import (
File "...\electrum\electrum\address_synchronizer.py", line 75, in <module>
class AddressSynchronizer(Logger, EventListener):
File "...\electrum\electrum\address_synchronizer.py", line 205, in AddressSynchronizer
def on_event_blockchain_updated(self, *args):
File "...\electrum\electrum\util.py", line 2005, in event_listener
classname, method_name = func.__qualname__.split('.')
ValueError: too many values to unpack (expected 2)
```
The hack needs to be applied before we try importing electrum_ecc, i.e. it needs to be in the main script.
However, it should also be applied if the main script is not invoked directly, but e.g. the user imports electrum directly.
Hence the duplication.
adds binary downloading from website if the specified signer is not one
of the two hardcoded signers with sftp access to the airlock server.
This makes it easier for other signers to run the script and create
signatures for spesmilo/electrum-signatures.
The user should still be able to open the Preferences dialog, despite the camera functionality being broken.
see https://github.com/spesmilo/electrum/issues/9949
```
6.36 | E | gui.qt.exception_window.Exception_Hook | exception caught by crash reporter
Traceback (most recent call last):
File "electrum\gui\qt\main_window.py", line 2672, in settings_dialog
File "electrum\gui\qt\settings_dialog.py", line 229, in __init__
File "electrum\gui\qt\qrreader\__init__.py", line 96, in find_system_cameras
File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
File "PyInstaller\loader\pyimod02_importers.py", line 450, in exec_module
File "electrum\gui\qt\qrreader\qtmultimedia\__init__.py", line 28, in <module>
File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
File "PyInstaller\loader\pyimod02_importers.py", line 450, in exec_module
File "electrum\gui\qt\qrreader\qtmultimedia\camera_dialog.py", line 32, in <module>
RuntimeError: PyQt6.QtMultimedia cannot import type '���d�⌂' from PyQt6.QtCore
```
The 'I Accept' button might not get enabled for some users where startup
is very slow.
The first check if the 'I Agree' button should be enabled gets fired
100ms after constructing the Dialog, which might not be enough.
So we can either remove the logic completely (done here) to prevent these
issues, or alternatively increase the initial timer to some large timout
after which the window should be assembled fully (e.g. 2 seconds). As
the user is not able to read the whole terms in few seconds this would
be a viable option too.
Explicitly sets the AA_DontShowIconsInMenus attribute to false as it
defaults to true on some Qt versions / environments which causes icons
to not show up in the menus (e.g. MacOS).
see https://forum.qt.io/post/813228
The attribute has to be set on self.app after the QApplication has been
instanciated (https://doc.qt.io/qt-6/qt.html#ApplicationAttribute-enum)
probably since qt6 migration many symlinks inside the .app in bundled PyQt were broken:
```
$ cp -r $HOME/electrum/dist/Electrum.app Electrum-ghost3.app
cp: /Users/vagrant/electrum/dist/Electrum.app/Contents/Resources/PyQt6/Qt6/lib/QtMultimediaQuick.framework/QtMultimediaQuick: No such file or directory
cp: /Users/vagrant/electrum/dist/Electrum.app/Contents/Resources/PyQt6/Qt6/lib/QtQuickTimeline.framework/QtQuickTimeline: No such file or directory
cp: /Users/vagrant/electrum/dist/Electrum.app/Contents/Resources/PyQt6/Qt6/lib/QtQuickControls2.framework/QtQuickControls2: No such file or directory
```
We were stripping out lots of datas/binaries from Qt from the mac build artifact, leaving behind dangling symlinks.
Instead of adding more hacks on top of the current hacks to also rm the dangling links, I tried to clean up this blacklisting.
There was no issue re the Windows build, no symlinks there, but I like to keep these two spec files in sync.
-----
Some numbers:
- mac:
- without any exclusions at all, the mac .dmg is 80 MiB.
- with these exclusions it is 57 MiB.
- win:
- (haven't built without exclusions.)
- with the previous stripping strategy, exes were 68M/68M/50M
- with these exclusions, exes are 66M/66M/50M
fixes exceptions ocurring when entering a '!' amount with a payment
identifier that is not allowed to send max (e.g. bolt11).
fixes exception when "Max" is still set from previous PI and the PI gets
replaced by one that doesn't allow max sending.
makes build not reproducible
random temporary paths leak into a compiled .so:
dist_ghost43/Electrum.app/Contents/Resources/propcache/_helpers_c.cpython-312-darwin.so