From 2ad49bbc5bf7ed12a44284f899cac0b68db8b38f Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sat, 13 Mar 2021 09:34:19 +0100 Subject: [PATCH] Kivy: Show fee dialog before opening a new channel. Remove fee and rbf from settings, as they are now always proposed --- .../gui/kivy/uix/dialogs/confirm_tx_dialog.py | 28 +++++++-------- .../uix/dialogs/lightning_open_channel.py | 29 ++++++++++------ electrum/gui/kivy/uix/dialogs/settings.py | 15 -------- electrum/gui/kivy/uix/screens.py | 10 ++++-- electrum/gui/qt/main_window.py | 18 +++++----- electrum/wallet.py | 34 +++++++++++-------- 6 files changed, 69 insertions(+), 65 deletions(-) diff --git a/electrum/gui/kivy/uix/dialogs/confirm_tx_dialog.py b/electrum/gui/kivy/uix/dialogs/confirm_tx_dialog.py index cf8e505fe..d01c77f88 100644 --- a/electrum/gui/kivy/uix/dialogs/confirm_tx_dialog.py +++ b/electrum/gui/kivy/uix/dialogs/confirm_tx_dialog.py @@ -97,11 +97,12 @@ Builder.load_string(''' on_release: popup.dismiss() Button: + id: ok_button text: _('OK') size_hint: 0.5, None height: '48dp' on_release: - root.pay() + root.on_pay(root.tx) popup.dismiss() ''') @@ -110,33 +111,35 @@ Builder.load_string(''' class ConfirmTxDialog(FeeSliderDialog, Factory.Popup): - def __init__(self, app: 'ElectrumWindow', invoice): + def __init__(self, app: 'ElectrumWindow', amount, make_tx, on_pay, *, show_final=True): Factory.Popup.__init__(self) FeeSliderDialog.__init__(self, app.electrum_config, self.ids.slider) self.app = app - self.show_final = bool(self.config.get('use_rbf')) - self.invoice = invoice + self.amount = amount + self.make_tx = make_tx + self.on_pay = on_pay + self.show_final = show_final self.update_slider() self.update_text() self.update_tx() def update_tx(self): - outputs = self.invoice.outputs + rbf = not bool(self.ids.final_cb.active) if self.show_final else False try: # make unsigned transaction - coins = self.app.wallet.get_spendable_coins(None) - tx = self.app.wallet.make_unsigned_transaction(coins=coins, outputs=outputs) + tx = self.make_tx(rbf) except NotEnoughFunds: self.warning = _("Not enough funds") + self.ids.ok_button.disabled = True return except Exception as e: - self.logger.exception('') + self.ids.ok_button.disabled = True + self.app.logger.exception('') self.app.show_error(repr(e)) return - rbf = not bool(self.ids.final_cb.active) if self.show_final else False - tx.set_rbf(rbf) - amount = sum(map(lambda x: x.value, outputs)) if '!' not in [x.value for x in outputs] else tx.output_value() + self.ids.ok_button.disabled = False + amount = self.amount if self.amount != '!' else tx.output_value() tx_size = tx.estimated_size() fee = tx.get_fee() feerate = Decimal(fee) / tx_size # sat/byte @@ -166,9 +169,6 @@ class ConfirmTxDialog(FeeSliderDialog, Factory.Popup): target, tooltip, dyn = self.config.get_fee_target() self.ids.fee_button.text = target - def pay(self): - self.app.protected(_('Send payment?'), self.app.send_screen.send_tx, (self.tx, self.invoice)) - def on_fee_button(self): fee_dialog = FeeDialog(self, self.config, self.after_fee_changed) fee_dialog.open() diff --git a/electrum/gui/kivy/uix/dialogs/lightning_open_channel.py b/electrum/gui/kivy/uix/dialogs/lightning_open_channel.py index 7e5904b7a..89f2309bf 100644 --- a/electrum/gui/kivy/uix/dialogs/lightning_open_channel.py +++ b/electrum/gui/kivy/uix/dialogs/lightning_open_channel.py @@ -12,6 +12,7 @@ from electrum.logging import Logger from electrum.lnutil import ln_dummy_address from .label_dialog import LabelDialog +from .confirm_tx_dialog import ConfirmTxDialog if TYPE_CHECKING: from ...main_window import ElectrumWindow @@ -174,20 +175,26 @@ class LightningOpenChannelDialog(Factory.Popup, Logger): else: conn_str = str(self.trampolines[self.pubkey]) amount = '!' if self.is_max else self.app.get_amount(self.amount) - self.app.protected('Create a new channel?', self.do_open_channel, (conn_str, amount)) self.dismiss() - - def do_open_channel(self, conn_str, amount, password): - coins = self.app.wallet.get_spendable_coins(None, nonlocal_only=True) lnworker = self.app.wallet.lnworker - try: - funding_tx = lnworker.mktx_for_open_channel(coins=coins, funding_sat=amount) - except Exception as e: - self.logger.exception("Problem opening channel") - self.app.show_error(_('Problem opening channel: ') + '\n' + repr(e)) - return + coins = self.app.wallet.get_spendable_coins(None, nonlocal_only=True) + make_tx = lambda rbf: lnworker.mktx_for_open_channel( + coins=coins, + funding_sat=amount, + fee_est=None) + on_pay = lambda tx: self.app.protected('Create a new channel?', self.do_open_channel, (tx, conn_str)) + d = ConfirmTxDialog( + self.app, + amount = amount, + make_tx=make_tx, + on_pay=on_pay, + show_final=False) + d.open() + + def do_open_channel(self, funding_tx, conn_str, password): # read funding_sat from tx; converts '!' to int value funding_sat = funding_tx.output_value_for_address(ln_dummy_address()) + lnworker = self.app.wallet.lnworker try: chan, funding_tx = lnworker.open_channel( connect_str=conn_str, @@ -196,7 +203,7 @@ class LightningOpenChannelDialog(Factory.Popup, Logger): push_amt_sat=0, password=password) except Exception as e: - self.logger.exception("Problem opening channel") + self.app.logger.exception("Problem opening channel") self.app.show_error(_('Problem opening channel: ') + '\n' + repr(e)) return n = chan.constraints.funding_txn_minimum_depth diff --git a/electrum/gui/kivy/uix/dialogs/settings.py b/electrum/gui/kivy/uix/dialogs/settings.py index 7aa2f3311..fbd5bac85 100644 --- a/electrum/gui/kivy/uix/dialogs/settings.py +++ b/electrum/gui/kivy/uix/dialogs/settings.py @@ -49,11 +49,6 @@ Builder.load_string(''' description: _("Base unit for Bitcoin amounts.") action: partial(root.unit_dialog, self) CardSeparator - SettingsItem: - title: _('Onchain fees') + ': ' + app.fee_status - description: _('Choose how transaction fees are estimated') - action: lambda dt: app.fee_dialog() - CardSeparator SettingsItem: status: root.fx_status() title: _('Fiat Currency') + ': ' + self.status @@ -66,16 +61,6 @@ Builder.load_string(''' description: _("Save and synchronize your labels.") action: partial(root.plugin_dialog, 'labels', self) CardSeparator - SettingsItem: - status: 'ON' if app.use_rbf else 'OFF' - title: _('Replace-by-fee') + ': ' + self.status - description: _("Create replaceable transactions.") - message: - _('If you check this box, your transactions will be marked as non-final,') \ - + ' ' + _('and you will have the possibility, while they are unconfirmed, to replace them with transactions that pays higher fees.') \ - + ' ' + _('Note that some merchants do not accept non-final transactions until they are confirmed.') - action: partial(root.boolean_dialog, 'use_rbf', _('Replace by fee'), self.message) - CardSeparator SettingsItem: status: _('Yes') if app.use_unconfirmed else _('No') title: _('Spend unconfirmed') + ': ' + self.status diff --git a/electrum/gui/kivy/uix/screens.py b/electrum/gui/kivy/uix/screens.py index 93b2d25d0..85bfba9b9 100644 --- a/electrum/gui/kivy/uix/screens.py +++ b/electrum/gui/kivy/uix/screens.py @@ -36,7 +36,7 @@ from electrum.lnutil import RECEIVED, SENT, PaymentFailure from electrum.logging import Logger from .dialogs.question import Question -from .dialogs.lightning_open_channel import LightningOpenChannelDialog +from .dialogs.confirm_tx_dialog import ConfirmTxDialog from electrum.gui.kivy import KIVY_GUI_PATH from electrum.gui.kivy.i18n import _ @@ -372,8 +372,12 @@ class SendScreen(CScreen, Logger): threading.Thread(target=pay_thread).start() def _do_pay_onchain(self, invoice: OnchainInvoice) -> None: - from .dialogs.confirm_tx_dialog import ConfirmTxDialog - d = ConfirmTxDialog(self.app, invoice) + outputs = invoice.outputs + amount = sum(map(lambda x: x.value, outputs)) if '!' not in [x.value for x in outputs] else '!' + coins = self.app.wallet.get_spendable_coins(None) + make_tx = lambda rbf: self.app.wallet.make_unsigned_transaction(coins=coins, outputs=outputs, rbf=rbf) + on_pay = lambda tx: self.app.protected(_('Send payment?'), self.send_tx, (tx, invoice)) + d = ConfirmTxDialog(self.app, amount=amount, make_tx=make_tx, on_pay=on_pay) d.open() def send_tx(self, tx, invoice, password): diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py index 6e65aced5..234e240cc 100644 --- a/electrum/gui/qt/main_window.py +++ b/electrum/gui/qt/main_window.py @@ -1796,9 +1796,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): def mktx_for_open_channel(self, funding_sat): coins = self.get_coins(nonlocal_only=True) - make_tx = lambda fee_est: self.wallet.lnworker.mktx_for_open_channel(coins=coins, - funding_sat=funding_sat, - fee_est=fee_est) + make_tx = lambda fee_est: self.wallet.lnworker.mktx_for_open_channel( + coins=coins, + funding_sat=funding_sat, + fee_est=fee_est) return make_tx def open_channel(self, connect_str, funding_sat, push_amt): @@ -1821,11 +1822,12 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): # read funding_sat from tx; converts '!' to int value funding_sat = funding_tx.output_value_for_address(ln_dummy_address()) def task(): - return self.wallet.lnworker.open_channel(connect_str=connect_str, - funding_tx=funding_tx, - funding_sat=funding_sat, - push_amt_sat=push_amt, - password=password) + return self.wallet.lnworker.open_channel( + connect_str=connect_str, + funding_tx=funding_tx, + funding_sat=funding_sat, + push_amt_sat=push_amt, + password=password) def on_success(args): chan, funding_tx = args n = chan.constraints.funding_txn_minimum_depth diff --git a/electrum/wallet.py b/electrum/wallet.py index 24a3a7c34..53ef7d878 100644 --- a/electrum/wallet.py +++ b/electrum/wallet.py @@ -183,8 +183,8 @@ async def sweep( fee: int = None, imax=100, locktime=None, - tx_version=None -) -> PartialTransaction: + tx_version=None) -> PartialTransaction: + inputs, keypairs = await sweep_preparations(privkeys, network, imax) total = sum(txin.value_sats() for txin in inputs) if fee is None: @@ -1236,9 +1236,14 @@ class Abstract_Wallet(AddressSynchronizer, ABC): assert is_address(selected_addr), f"not valid bitcoin address: {selected_addr}" return selected_addr - def make_unsigned_transaction(self, *, coins: Sequence[PartialTxInput], - outputs: List[PartialTxOutput], fee=None, - change_addr: str = None, is_sweep=False) -> PartialTransaction: + def make_unsigned_transaction( + self, *, + coins: Sequence[PartialTxInput], + outputs: List[PartialTxOutput], + fee=None, + change_addr: str = None, + is_sweep=False, + rbf=False) -> PartialTransaction: if any([c.already_has_some_signatures() for c in coins]): raise Exception("Some inputs already contain signatures!") @@ -1298,12 +1303,13 @@ class Abstract_Wallet(AddressSynchronizer, ABC): old_change_addrs = [] # change address. if empty, coin_chooser will set it change_addrs = self.get_change_addresses_for_new_transaction(change_addr or old_change_addrs) - tx = coin_chooser.make_tx(coins=coins, - inputs=txi, - outputs=list(outputs) + txo, - change_addrs=change_addrs, - fee_estimator_vb=fee_estimator, - dust_threshold=self.dust_threshold()) + tx = coin_chooser.make_tx( + coins=coins, + inputs=txi, + outputs=list(outputs) + txo, + change_addrs=change_addrs, + fee_estimator_vb=fee_estimator, + dust_threshold=self.dust_threshold()) else: # "spend max" branch # note: This *will* spend inputs with negative effective value (if there are any). @@ -1325,7 +1331,7 @@ class Abstract_Wallet(AddressSynchronizer, ABC): # Timelock tx to current height. tx.locktime = get_locktime_for_new_transaction(self.network) - tx.set_rbf(False) # caller can set RBF manually later + tx.set_rbf(rbf) tx.add_info_from_wallet(self) run_hook('make_unsigned_transaction', self, tx) return tx @@ -1340,8 +1346,8 @@ class Abstract_Wallet(AddressSynchronizer, ABC): coins=coins, outputs=outputs, fee=fee, - change_addr=change_addr) - tx.set_rbf(rbf) + change_addr=change_addr, + rbf=rbf) if tx_version is not None: tx.version = tx_version if sign: