RunCoroutineDialog has a run() method that blocks the thread
without blocking the GUI (using exec), and a Cancel button
that cancels the coroutine.
main_window.run_coroutine_dialog() is a wrapper that returns
the coroutine result and may raise exceptions.
BlockingWaitingDialog was removed is transaction_dialog,
where it was not particularly useful.
- rename globals
- also rm hardcoded strings from qml
- use consistent unit names in qml
(previously mixed sat/vB and sat/byte (latter coming from core lib))
- risk_of_burning_coins_as_fees is turned into a private (helper) method, only called by check_sighash.
UIs should only care about check_sighash.
- check_sighash returns instance of new class "TxSighashDanger" instead of tuple
- made warning levels more fine-grained (FEE_WARNING_SKIPCONFIRM vs FEE_WARNING_NEEDCONFIRM)
- this became more complicated than I had hoped for but I think it is worth it to ~merge
check_sighash and risk_of_burning_coins_as_fees into one.
related https://github.com/spesmilo/electrum/pull/8699
qml: use backend sighash check and add user confirmation path
qt: use backend sighash check and add user confirmation path
qml: include get_warning_for_risk_of_burning_coins_as_fees test in txdetails
qt: add warning icon to sighash warning
add sighash and fee checks to wallet.sign_transaction, making all warnings fatal unless ignore_warnings is set to True
tests: test sign_transaction on both code paths with ignore_warnings True and False,
raise specific exceptions TransactionPotentiallyDangerousException and TransactionDangerousException
Somewhat a follow-up to 649ce979ab.
This adds some safety belts so we don't accidentally sign a tx that
contains a dummy address.
Specifically we check that tx does not contain output for dummy addr:
- in wallet.sign_transaction
- in network.broadcast_transaction
The second one is perhaps redundant, but I think it does not hurt.
lnworker is None if lightning is disabled.
follow-up 649ce979ab
```
15.14 | E | gui.qt.exception_window.Exception_Hook | exception caught by crash reporter
Traceback (most recent call last):
File "...\electrum\electrum\gui\qt\rate_limiter.py", line 231, in wrapper
return RateLimiter.invoke(rate, ts_after, func, args, kwargs)
File "...\electrum\electrum\gui\qt\rate_limiter.py", line 79, in invoke
return rl._invoke(args, kwargs)
File "...\electrum\electrum\gui\qt\rate_limiter.py", line 91, in _invoke
return self._doIt()
File "...\electrum\electrum\gui\qt\rate_limiter.py", line 120, in _doIt
retval = self.func(*args, **kwargs) # and.. call the function. use latest invocation's args
File "...\electrum\electrum\gui\qt\transaction_dialog.py", line 745, in _throttled_update
self.update()
File "...\electrum\electrum\gui\qt\transaction_dialog.py", line 750, in update
self.io_widget.update(self.tx)
File "...\electrum\electrum\gui\qt\transaction_dialog.py", line 243, in update
insert_tx_io(
File "...\electrum\electrum\gui\qt\transaction_dialog.py", line 205, in insert_tx_io
tcf_addr = addr_text_format(addr)
File "...\electrum\electrum\gui\qt\transaction_dialog.py", line 173, in addr_text_format
sm = self.wallet.lnworker.swap_manager
AttributeError: 'NoneType' object has no attribute 'swap_manager'
```
```
229.18 | E | gui.qt.main_window.[test_segwit_2] | on_error
Traceback (most recent call last):
File "...\electrum\gui\qt\util.py", line 917, in run
result = task.task()
File "...\electrum\gui\qt\send_tab.py", line 681, in broadcast_thread
if self.payto_e.payment_identifier.has_expired():
AttributeError: 'NoneType' object has no attribute 'has_expired'
```
In SendTab.broadcast_transaction.broadcast_thread, self.payto_e.payment_identifier was referenced -
but do_clear() has already cleared it by then.
E.g. consider SendTab.pay_onchain_dialog: it calls save_pending_invoice(), which calls do_clear(),
and later (in sign_done), it calls window.broadcast_or_show, which will call SendTab.broadcast_transaction().
As there might be multiple independent transaction dialogs open simultaneously, the single shared state
send_tab.payto_e.payment_identifier approach was problematic -- I think it is conceptually nicer to
pass around the payment_identifiers as needed, as done with this change.
However, this change is not a full proper fix, as it still somewhat relies on
send_tab.payto_e.payment_identifier (e.g. in pay_onchain_dialog). Hence, e.g. when using
the invoice_list context menu "Pay..." item, as payto_e.payment_identifier is not set,
payment_identifier will be None in broadcast_transaction.
but at least we handle PI being None gracefully -- before this change, broadcast_transaction
expected PI to be set, and it was never set to the correct thing (as do_clear() already ran by then):
depending on timing it was either None or a new empty PI. In the former case, producing the above
traceback and hard failing (not only for bip70 stuff!), and in the latter, silently ignoring the logic bug.
A new config API is introduced, and ~all of the codebase is adapted to it.
The old API is kept but mainly only for dynamic usage where its extra flexibility is needed.
Using examples, the old config API looked this:
```
>>> config.get("request_expiry", 86400)
604800
>>> config.set_key("request_expiry", 86400)
>>>
```
The new config API instead:
```
>>> config.WALLET_PAYREQ_EXPIRY_SECONDS
604800
>>> config.WALLET_PAYREQ_EXPIRY_SECONDS = 86400
>>>
```
The old API operated on arbitrary string keys, the new one uses
a static ~enum-like list of variables.
With the new API:
- there is a single centralised list of config variables, as opposed to
these being scattered all over
- no more duplication of default values (in the getters)
- there is now some (minimal for now) type-validation/conversion for
the config values
closes https://github.com/spesmilo/electrum/pull/5640
closes https://github.com/spesmilo/electrum/pull/5649
Note: there is yet a third API added here, for certain niche/abstract use-cases,
where we need a reference to the config variable itself.
It should only be used when needed:
```
>>> var = config.cv.WALLET_PAYREQ_EXPIRY_SECONDS
>>> var
<ConfigVarWithConfig key='request_expiry'>
>>> var.get()
604800
>>> var.set(3600)
>>> var.get_default_value()
86400
>>> var.is_set()
True
>>> var.is_modifiable()
True
```
When exporting a tx as qr code, the prev txs are omitted to save space.
This causes problems with offline signers: software electrum signers will
just warn and then proceed, but hw devices will typically error.
This allows users to edit labels from the utxo_dialog,
without having to search for the transaction in history.
Also, remove block hash from tx dialog: not very useful, and
available through block explorers. (the situation where this
could be useful is case of a chain fork, but in that case the
tx might be mined in both branches of the fork, and we would
want to know that).
If checked, we download prev (parent) txs from the network, asynchronously.
This allows calculating the fee and showing "input addresses".
We could also SPV-verify the tx, to fill in missing tx_mined_status
(block height, blockhash, timestamp, short ids), but this is not done currently.
Note that there is no clean way to do this with electrum protocol 1.4:
`blockchain.transaction.get_merkle(tx_hash, height)` requires knowledge of the block height.
Loosely based on 6112fe0e51
- wallet.add_input_info() previously had a fallback to download parent
prev txs from the network (after a lookup in wallet.db failed).
wallet.add_input_info() is not async, so the network request cannot
be done cleanly there and was really just a hack.
- tx.add_info_from_wallet() calls wallet.add_input_info() on each txin,
in which case these network requests were done sequentially, not concurrently
- the network part of wallet.add_input_info() is now split out into new method:
txin.add_info_from_network()
- in addition to tx.add_info_from_wallet(), there is now also tx.add_info_from_network()
- callers of old tx.add_info_from_wallet() should now called either
- tx.add_info_from_wallet(), then tx.add_info_from_network(), preferably in that order
- tx.add_info_from_wallet() alone is sufficient if the tx is complete,
or typically when not in a signing context
- callers of wallet.bump_fee and wallet.dscancel are now expected to have already
called tx.add_info_from_network(), as it cannot be done in a non-async context
(but for the common case of all-inputs-are-ismine, bump_fee/dscancel should work regardless)
- PartialTxInput.utxo was moved to the baseclass, TxInput.utxo
That way, users can see the effects settings directly on their transaction.
This changes the API of make_tx:
- get_coins is called inside make_tx, so that inputs can be changed dynamically
- make_tx takes an optional parameter: unconfirmed_only, passed to get_coins
- ConfirmTxDialog detects if we can pay by disabling confirmed_only or lowering fee
Traceback (most recent call last):
File "/home/user/wspace/electrum/electrum/gui/qt/transaction_dialog.py", line 255, in _open_internal_link
self.main_window.do_process_from_txid(txid=target, parent=self)
File "/home/user/wspace/electrum/electrum/gui/qt/main_window.py", line 2212, in do_process_from_txid
self.show_transaction(tx)
File "/home/user/wspace/electrum/electrum/gui/qt/main_window.py", line 1079, in show_transaction
show_transaction(tx, parent=self, desc=tx_desc)
File "/home/user/wspace/electrum/electrum/gui/qt/transaction_dialog.py", line 351, in show_transaction
d = TxDialog(tx, parent=parent, desc=desc, prompt_if_unsaved=prompt_if_unsaved)
File "/home/user/wspace/electrum/electrum/gui/qt/transaction_dialog.py", line 449, in __init__
self.update()
File "/home/user/wspace/electrum/electrum/gui/qt/transaction_dialog.py", line 667, in update
tx_mined_status = self.wallet.lnworker.lnwatcher.adb.get_tx_height(txid)
AttributeError: 'NoneType' object has no attribute 'adb'
```
629.08 | E | gui.qt.exception_window.Exception_Hook | exception caught by crash reporter
Traceback (most recent call last):
File ".../electrum/electrum/gui/qt/invoice_list.py", line 170, in <lambda>
menu.addAction(_("Pay") + "...", lambda: self.send_tab.do_pay_invoice(invoice))
File ".../electrum/electrum/gui/qt/send_tab.py", line 573, in do_pay_invoice
self.pay_onchain_dialog(self.window.get_coins(), invoice.outputs)
File ".../electrum/electrum/gui/qt/send_tab.py", line 251, in pay_onchain_dialog
self.window.show_transaction(tx)
File ".../electrum/electrum/gui/qt/main_window.py", line 1074, in show_transaction
show_transaction(tx, parent=self, desc=tx_desc)
File ".../electrum/electrum/gui/qt/transaction_dialog.py", line 351, in show_transaction
d = TxDialog(tx, parent=parent, desc=desc, prompt_if_unsaved=prompt_if_unsaved)
File ".../electrum/electrum/gui/qt/transaction_dialog.py", line 450, in __init__
self.set_title()
File ".../electrum/electrum/gui/qt/transaction_dialog.py", line 858, in set_title
self.setWindowTitle(_("Transaction") + ' ' + self.tx.txid())
TypeError: can only concatenate str (not "NoneType") to str
```
```
Traceback (most recent call last):
File "/home/user/wspace/electrum/electrum/gui/qt/invoice_list.py", line 169, in <lambda>
menu.addAction(_("Pay") + "...", lambda: self.send_tab.do_pay_invoice(invoice))
File "/home/user/wspace/electrum/electrum/gui/qt/send_tab.py", line 585, in do_pay_invoice
self.pay_onchain_dialog(self.window.get_coins(), invoice.outputs)
File "/home/user/wspace/electrum/electrum/gui/qt/send_tab.py", line 271, in pay_onchain_dialog
preview_dlg = PreviewTxDialog(
File "/home/user/wspace/electrum/electrum/gui/qt/transaction_dialog.py", line 962, in __init__
self.update()
File "/home/user/wspace/electrum/electrum/gui/qt/transaction_dialog.py", line 658, in update
self.io_widget.update(self.tx)
File "/home/user/wspace/electrum/electrum/gui/qt/transaction_dialog.py", line 212, in update
tx_height, tx_pos = self.wallet.adb.get_txpos(self.tx.txid())
File "/home/user/wspace/electrum/electrum/address_synchronizer.py", line 483, in get_txpos
verified_tx_mined_info = self.db.get_verified_tx(tx_hash)
File "/home/user/wspace/electrum/electrum/json_db.py", line 44, in wrapper
return func(self, *args, **kwargs)
File "/home/user/wspace/electrum/electrum/wallet_db.py", line 1256, in get_verified_tx
assert isinstance(txid, str)
AssertionError
```
textedit.setMinimumWidth(950) (from 5af399d19639b0c141398db964270c4974f124acdoes) does not play well with the tx_dlg.setMinimumWidth(640)
when resizing. Make 950 only affect the default sizing of the textedit.
- this widget will be used in various dialogs
- making this change now will prevent downstream conflicts
- the part that displays "coin selection active" was commented out