While attempting to reproducibly build the qml android apk, one of the differences
was due to the "frozenlist" dependency (pulled in by aiohttp) - the compiled C parts
were not deterministic. By setting this env var, we can opt-out [0] of all the C
accelerated parts and just use the pure-python implementation. We are already doing
the same for other aiohttp-related packages anyway.
[0]: c2794cac12/setup.py (L7)
fixes https://github.com/spesmilo/electrum/issues/7780
fixes https://github.com/spesmilo/electrum/issues/7815
Re FIXME in main_window.py, in particular, adb might call `add_transaction` on the same tx multiple times.
In `wallet.on_event_adb_added_tx`, maybe we should propagate `notify_GUI` to `wallet._update_request_statuses_touched_by_tx`.
The issue being fixed here (above TARS reports) can be triggered in multiple ways, e.g.:
- have an already paid receive request, and receive a payment to the same address again
- have an already paid receive request, and *spend from* that address (in which case the history of the address will change, and address_synchronizer will call add_transaction again on the old tx that satisfied the old receive request)
related: 5e0df77df6
kivy 2.1 seemingly became more sensitive to threading issues.
This used to work on kivy 2.0 and older, but 2.1 is complaining.
```
E | lnworker.LNWallet.[default_wallet] | Exception in pay_invoice: TypeError('Cannot create graphics instruction outside the main Kivy thread')
Traceback (most recent call last):
File "...\electrum\util.py", line 1184, in wrapper
return await func(*args, **kwargs)
File "...\electrum\lnworker.py", line 1178, in pay_invoice
util.trigger_callback('payment_succeeded', self.wallet, key)
File "...\electrum\util.py", line 1633, in trigger_callback
callback(*args)
File "...\electrum\gui\kivy\main_window.py", line 309, in on_event_payment_succeeded
self.show_info(_('Payment succeeded') + '\n\n' + description)
File "...\electrum\gui\kivy\main_window.py", line 1105, in show_info
self.show_error(error, icon=f'atlas://{KIVY_GUI_PATH}/theming/atlas/light/important',
File "...\electrum\gui\kivy\main_window.py", line 1097, in show_error
self.show_info_bubble(text=error, icon=icon, width=width,
File "...\electrum\gui\kivy\main_window.py", line 1123, in show_info_bubble
info_bubble = self.info_bubble = Factory.InfoBubble()
File "...\Python310\site-packages\kivy\uix\bubble.py", line 199, in __init__
self._arrow_layout = BoxLayout()
File "...\Python310\site-packages\kivy\uix\boxlayout.py", line 145, in __init__
super(BoxLayout, self).__init__(**kwargs)
File "...\Python310\site-packages\kivy\uix\layout.py", line 76, in __init__
super(Layout, self).__init__(**kwargs)
File "...\Python310\site-packages\kivy\uix\widget.py", line 361, in __init__
self.canvas = Canvas(opacity=self.opacity)
File "kivy\graphics\instructions.pyx", line 608, in kivy.graphics.instructions.Canvas.__init__
File "kivy\graphics\instructions.pyx", line 154, in kivy.graphics.instructions.InstructionGroup.__init__
File "kivy\graphics\instructions.pyx", line 60, in kivy.graphics.instructions.Instruction.__init__
TypeError: Cannot create graphics instruction outside the main Kivy thread
E | gui.kivy.uix.dialogs.crash_reporter.ExceptionHook | exception caught by crash reporter
Traceback (most recent call last):
File "...\electrum\gui\kivy\uix\screens.py", line 419, in pay_thread
fut.result()
File "...\Python310\lib\concurrent\futures\_base.py", line 446, in result
return self.__get_result()
File "...\Python310\lib\concurrent\futures\_base.py", line 391, in __get_result
raise self._exception
File "...\electrum\util.py", line 1184, in wrapper
return await func(*args, **kwargs)
File "...\electrum\lnworker.py", line 1178, in pay_invoice
util.trigger_callback('payment_succeeded', self.wallet, key)
File "...\electrum\util.py", line 1633, in trigger_callback
callback(*args)
File "...\electrum\gui\kivy\main_window.py", line 309, in on_event_payment_succeeded
self.show_info(_('Payment succeeded') + '\n\n' + description)
File "...\electrum\gui\kivy\main_window.py", line 1105, in show_info
self.show_error(error, icon=f'atlas://{KIVY_GUI_PATH}/theming/atlas/light/important',
File "...\electrum\gui\kivy\main_window.py", line 1097, in show_error
self.show_info_bubble(text=error, icon=icon, width=width,
File "...\electrum\gui\kivy\main_window.py", line 1123, in show_info_bubble
info_bubble = self.info_bubble = Factory.InfoBubble()
File "...\Python310\site-packages\kivy\uix\bubble.py", line 199, in __init__
self._arrow_layout = BoxLayout()
File "...\Python310\site-packages\kivy\uix\boxlayout.py", line 145, in __init__
super(BoxLayout, self).__init__(**kwargs)
File "...\Python310\site-packages\kivy\uix\layout.py", line 76, in __init__
super(Layout, self).__init__(**kwargs)
File "...\Python310\site-packages\kivy\uix\widget.py", line 361, in __init__
self.canvas = Canvas(opacity=self.opacity)
File "kivy\graphics\instructions.pyx", line 608, in kivy.graphics.instructions.Canvas.__init__
File "kivy\graphics\instructions.pyx", line 154, in kivy.graphics.instructions.InstructionGroup.__init__
File "kivy\graphics\instructions.pyx", line 60, in kivy.graphics.instructions.Instruction.__init__
TypeError: Cannot create graphics instruction outside the main Kivy thread
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "...\electrum\util.py", line 1128, in run_with_except_hook
run_original(*args2, **kwargs2)
File "...\Python310\lib\threading.py", line 946, in run
self._target(*self._args, **self._kwargs)
File "...\electrum\gui\kivy\uix\screens.py", line 421, in pay_thread
self.app.show_error(repr(e))
File "...\electrum\gui\kivy\main_window.py", line 1097, in show_error
self.show_info_bubble(text=error, icon=icon, width=width,
File "...\electrum\gui\kivy\main_window.py", line 1123, in show_info_bubble
info_bubble = self.info_bubble = Factory.InfoBubble()
File "...\Python310\site-packages\kivy\uix\bubble.py", line 199, in __init__
self._arrow_layout = BoxLayout()
File "...\Python310\site-packages\kivy\uix\boxlayout.py", line 145, in __init__
super(BoxLayout, self).__init__(**kwargs)
File "...\Python310\site-packages\kivy\uix\layout.py", line 76, in __init__
super(Layout, self).__init__(**kwargs)
File "...\Python310\site-packages\kivy\uix\widget.py", line 361, in __init__
self.canvas = Canvas(opacity=self.opacity)
File "kivy\graphics\instructions.pyx", line 608, in kivy.graphics.instructions.Canvas.__init__
File "kivy\graphics\instructions.pyx", line 154, in kivy.graphics.instructions.InstructionGroup.__init__
File "kivy\graphics\instructions.pyx", line 60, in kivy.graphics.instructions.Instruction.__init__
TypeError: Cannot create graphics instruction outside the main Kivy thread
Exception in thread Thread-2 (pay_thread):
Traceback (most recent call last):
File "...\electrum\gui\kivy\uix\screens.py", line 419, in pay_thread
fut.result()
File "...\Python310\lib\concurrent\futures\_base.py", line 446, in result
return self.__get_result()
File "...\Python310\lib\concurrent\futures\_base.py", line 391, in __get_result
raise self._exception
File "...\electrum\util.py", line 1184, in wrapper
return await func(*args, **kwargs)
File "...\electrum\lnworker.py", line 1178, in pay_invoice
util.trigger_callback('payment_succeeded', self.wallet, key)
File "...\electrum\util.py", line 1633, in trigger_callback
callback(*args)
File "...\electrum\gui\kivy\main_window.py", line 309, in on_event_payment_succeeded
self.show_info(_('Payment succeeded') + '\n\n' + description)
File "...\electrum\gui\kivy\main_window.py", line 1105, in show_info
self.show_error(error, icon=f'atlas://{KIVY_GUI_PATH}/theming/atlas/light/important',
File "...\electrum\gui\kivy\main_window.py", line 1097, in show_error
self.show_info_bubble(text=error, icon=icon, width=width,
File "...\electrum\gui\kivy\main_window.py", line 1123, in show_info_bubble
info_bubble = self.info_bubble = Factory.InfoBubble()
File "...\Python310\site-packages\kivy\uix\bubble.py", line 199, in __init__
self._arrow_layout = BoxLayout()
File "...\Python310\site-packages\kivy\uix\boxlayout.py", line 145, in __init__
super(BoxLayout, self).__init__(**kwargs)
File "...\Python310\site-packages\kivy\uix\layout.py", line 76, in __init__
super(Layout, self).__init__(**kwargs)
File "...\Python310\site-packages\kivy\uix\widget.py", line 361, in __init__
self.canvas = Canvas(opacity=self.opacity)
File "kivy\graphics\instructions.pyx", line 608, in kivy.graphics.instructions.Canvas.__init__
File "kivy\graphics\instructions.pyx", line 154, in kivy.graphics.instructions.InstructionGroup.__init__
File "kivy\graphics\instructions.pyx", line 60, in kivy.graphics.instructions.Instruction.__init__
TypeError: Cannot create graphics instruction outside the main Kivy thread
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "...\electrum\util.py", line 1128, in run_with_except_hook
run_original(*args2, **kwargs2)
File "...\Python310\lib\threading.py", line 946, in run
self._target(*self._args, **self._kwargs)
File "...\electrum\gui\kivy\uix\screens.py", line 421, in pay_thread
self.app.show_error(repr(e))
File "...\electrum\gui\kivy\main_window.py", line 1097, in show_error
self.show_info_bubble(text=error, icon=icon, width=width,
File "...\electrum\gui\kivy\main_window.py", line 1123, in show_info_bubble
info_bubble = self.info_bubble = Factory.InfoBubble()
File "...\Python310\site-packages\kivy\uix\bubble.py", line 199, in __init__
self._arrow_layout = BoxLayout()
File "...\Python310\site-packages\kivy\uix\boxlayout.py", line 145, in __init__
super(BoxLayout, self).__init__(**kwargs)
File "...\Python310\site-packages\kivy\uix\layout.py", line 76, in __init__
super(Layout, self).__init__(**kwargs)
File "...\Python310\site-packages\kivy\uix\widget.py", line 361, in __init__
self.canvas = Canvas(opacity=self.opacity)
File "kivy\graphics\instructions.pyx", line 608, in kivy.graphics.instructions.Canvas.__init__
File "kivy\graphics\instructions.pyx", line 154, in kivy.graphics.instructions.InstructionGroup.__init__
File "kivy\graphics\instructions.pyx", line 60, in kivy.graphics.instructions.Instruction.__init__
TypeError: Cannot create graphics instruction outside the main Kivy thread
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "...\Python310\lib\threading.py", line 1009, in _bootstrap_inner
self.run()
File "...\electrum\util.py", line 1130, in run_with_except_hook
sys.excepthook(*sys.exc_info())
File "...\electrum\gui\kivy\uix\dialogs\crash_reporter.py", line 188, in <lambda>
sys.excepthook = lambda exctype, value, tb: self.handle_exception(value)
File "...\electrum\gui\kivy\uix\dialogs\crash_reporter.py", line 199, in handle_exception
e = CrashReporter(self.main_window, *exc_info)
File "...\electrum\gui\kivy\uix\dialogs\crash_reporter.py", line 100, in __init__
Factory.Popup.__init__(self)
File "...\Python310\site-packages\kivy\uix\modalview.py", line 195, in __init__
super(ModalView, self).__init__(**kwargs)
File "...\Python310\site-packages\kivy\uix\anchorlayout.py", line 68, in __init__
super(AnchorLayout, self).__init__(**kwargs)
File "...\Python310\site-packages\kivy\uix\layout.py", line 76, in __init__
super(Layout, self).__init__(**kwargs)
File "...\Python310\site-packages\kivy\uix\widget.py", line 361, in __init__
self.canvas = Canvas(opacity=self.opacity)
File "kivy\graphics\instructions.pyx", line 608, in kivy.graphics.instructions.Canvas.__init__
File "kivy\graphics\instructions.pyx", line 154, in kivy.graphics.instructions.InstructionGroup.__init__
File "kivy\graphics\instructions.pyx", line 60, in kivy.graphics.instructions.Instruction.__init__
TypeError: Cannot create graphics instruction outside the main Kivy thread
```
- in lnurl.py, make request methods async
- in Qt GUI, lnurl network requests no longer block the GUI thread
- but they still do in the kivy GUI
- "lightning address" (LUD-16) support is removed for now as the
email addresses are indistinguishable from openalias email addresses
(both protocols should have added and enforced a prefix, or similar,
to remove this kind of ambiguity -- now we would need to make a
network request just to identify what kind of ID we were given)
- add param to _check_text to toggle if network requests are allowed ("full check")
- every 0.5 sec, if the textedit has no focus, do full check
- on textChanged()
- detect if user copy-pasted by comparing current text against clipboard contents,
if so, do full check
- otherwise, do partial check
- on clicking ButtonsWidget btns (scan qr, paste, read file), do full check
follow-up e1d34300e5
follow-up dc15d59fcf
```
Traceback (most recent call last):
File ".../electrum/gui/qt/main_window.py", line 910, in timer_actions
self.update_wallet()
File ".../electrum/gui/qt/main_window.py", line 1068, in update_wallet
self.update_tabs()
File ".../electrum/gui/qt/main_window.py", line 1075, in update_tabs
self.history_model.refresh('update_tabs')
File ".../electrum/util.py", line 439, in <lambda>
return lambda *args, **kw_args: do_profile(args, kw_args)
File ".../electrum/util.py", line 435, in do_profile
o = func(*args, **kw_args)
File ".../electrum/gui/qt/history_list.py", line 275, in refresh
transactions = wallet.get_full_history(
File ".../electrum/util.py", line 439, in <lambda>
return lambda *args, **kw_args: do_profile(args, kw_args)
File ".../electrum/util.py", line 435, in do_profile
o = func(*args, **kw_args)
File ".../electrum/wallet.py", line 1109, in get_full_history
lightning_history = self.lnworker.get_lightning_history() if self.lnworker and include_lightning else {}
File ".../electrum/lnworker.py", line 839, in get_lightning_history
assert direction == PaymentDirection.FORWARDING
AssertionError
```
follow-up fe74e4b9c9
Just for sanity -- there could be a collision of class names (defined in separate modules).
```
>>> wallet.is_up_to_date.__qualname__
'Abstract_Wallet.is_up_to_date'
>>> wallet.is_up_to_date.__module__
'electrum.wallet'
```
and use that registration in the iterator.
That way, we do not invoke getattr on arbitrary methods, which
could trigger unwanted code execution (for example, if a method
is decorated with @property, getattr will execute its code).
- the detection of self-payments was using the length
of the htlc list, incorrectly categorizing all MPPs.
- the fee for self-payments was not correctly computed.
get_unused_addresses() has been broken since #7730, because
addresses are considered as permanently used if they are in
the list of keys of receive_requests. This is true even if
an address is used as fallback for a lightning payment. This
means that the number of lightning payments we can receive
is constrained by the gap limit.
If a payment succeeds off-chain, we want to be able to reuse
its fallback address in other requests (this does not reduce
privacy, because invoices already share the same public key).
This implies that we should not use the onchain address as key
for lightning-enabled requests in wallet.receive_requests. If
we did, paid invoices would be overwritten when the address is
reused. That is the reason for the wallet_db upgrade.
Related: a3faf85e3c