Browse Source

Kivy: Show fee dialog before opening a new channel.

Remove fee and rbf from settings, as they are now always proposed
patch-4
ThomasV 4 years ago
parent
commit
2ad49bbc5b
  1. 28
      electrum/gui/kivy/uix/dialogs/confirm_tx_dialog.py
  2. 29
      electrum/gui/kivy/uix/dialogs/lightning_open_channel.py
  3. 15
      electrum/gui/kivy/uix/dialogs/settings.py
  4. 10
      electrum/gui/kivy/uix/screens.py
  5. 18
      electrum/gui/qt/main_window.py
  6. 34
      electrum/wallet.py

28
electrum/gui/kivy/uix/dialogs/confirm_tx_dialog.py

@ -97,11 +97,12 @@ Builder.load_string('''
on_release: on_release:
popup.dismiss() popup.dismiss()
Button: Button:
id: ok_button
text: _('OK') text: _('OK')
size_hint: 0.5, None size_hint: 0.5, None
height: '48dp' height: '48dp'
on_release: on_release:
root.pay() root.on_pay(root.tx)
popup.dismiss() popup.dismiss()
''') ''')
@ -110,33 +111,35 @@ Builder.load_string('''
class ConfirmTxDialog(FeeSliderDialog, Factory.Popup): 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) Factory.Popup.__init__(self)
FeeSliderDialog.__init__(self, app.electrum_config, self.ids.slider) FeeSliderDialog.__init__(self, app.electrum_config, self.ids.slider)
self.app = app self.app = app
self.show_final = bool(self.config.get('use_rbf')) self.amount = amount
self.invoice = invoice self.make_tx = make_tx
self.on_pay = on_pay
self.show_final = show_final
self.update_slider() self.update_slider()
self.update_text() self.update_text()
self.update_tx() self.update_tx()
def update_tx(self): def update_tx(self):
outputs = self.invoice.outputs rbf = not bool(self.ids.final_cb.active) if self.show_final else False
try: try:
# make unsigned transaction # make unsigned transaction
coins = self.app.wallet.get_spendable_coins(None) tx = self.make_tx(rbf)
tx = self.app.wallet.make_unsigned_transaction(coins=coins, outputs=outputs)
except NotEnoughFunds: except NotEnoughFunds:
self.warning = _("Not enough funds") self.warning = _("Not enough funds")
self.ids.ok_button.disabled = True
return return
except Exception as e: except Exception as e:
self.logger.exception('') self.ids.ok_button.disabled = True
self.app.logger.exception('')
self.app.show_error(repr(e)) self.app.show_error(repr(e))
return return
rbf = not bool(self.ids.final_cb.active) if self.show_final else False self.ids.ok_button.disabled = False
tx.set_rbf(rbf) amount = self.amount if self.amount != '!' else tx.output_value()
amount = sum(map(lambda x: x.value, outputs)) if '!' not in [x.value for x in outputs] else tx.output_value()
tx_size = tx.estimated_size() tx_size = tx.estimated_size()
fee = tx.get_fee() fee = tx.get_fee()
feerate = Decimal(fee) / tx_size # sat/byte feerate = Decimal(fee) / tx_size # sat/byte
@ -166,9 +169,6 @@ class ConfirmTxDialog(FeeSliderDialog, Factory.Popup):
target, tooltip, dyn = self.config.get_fee_target() target, tooltip, dyn = self.config.get_fee_target()
self.ids.fee_button.text = 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): def on_fee_button(self):
fee_dialog = FeeDialog(self, self.config, self.after_fee_changed) fee_dialog = FeeDialog(self, self.config, self.after_fee_changed)
fee_dialog.open() fee_dialog.open()

29
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 electrum.lnutil import ln_dummy_address
from .label_dialog import LabelDialog from .label_dialog import LabelDialog
from .confirm_tx_dialog import ConfirmTxDialog
if TYPE_CHECKING: if TYPE_CHECKING:
from ...main_window import ElectrumWindow from ...main_window import ElectrumWindow
@ -174,20 +175,26 @@ class LightningOpenChannelDialog(Factory.Popup, Logger):
else: else:
conn_str = str(self.trampolines[self.pubkey]) conn_str = str(self.trampolines[self.pubkey])
amount = '!' if self.is_max else self.app.get_amount(self.amount) 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() 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 lnworker = self.app.wallet.lnworker
try: coins = self.app.wallet.get_spendable_coins(None, nonlocal_only=True)
funding_tx = lnworker.mktx_for_open_channel(coins=coins, funding_sat=amount) make_tx = lambda rbf: lnworker.mktx_for_open_channel(
except Exception as e: coins=coins,
self.logger.exception("Problem opening channel") funding_sat=amount,
self.app.show_error(_('Problem opening channel: ') + '\n' + repr(e)) fee_est=None)
return 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 # read funding_sat from tx; converts '!' to int value
funding_sat = funding_tx.output_value_for_address(ln_dummy_address()) funding_sat = funding_tx.output_value_for_address(ln_dummy_address())
lnworker = self.app.wallet.lnworker
try: try:
chan, funding_tx = lnworker.open_channel( chan, funding_tx = lnworker.open_channel(
connect_str=conn_str, connect_str=conn_str,
@ -196,7 +203,7 @@ class LightningOpenChannelDialog(Factory.Popup, Logger):
push_amt_sat=0, push_amt_sat=0,
password=password) password=password)
except Exception as e: 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)) self.app.show_error(_('Problem opening channel: ') + '\n' + repr(e))
return return
n = chan.constraints.funding_txn_minimum_depth n = chan.constraints.funding_txn_minimum_depth

15
electrum/gui/kivy/uix/dialogs/settings.py

@ -49,11 +49,6 @@ Builder.load_string('''
description: _("Base unit for Bitcoin amounts.") description: _("Base unit for Bitcoin amounts.")
action: partial(root.unit_dialog, self) action: partial(root.unit_dialog, self)
CardSeparator CardSeparator
SettingsItem:
title: _('Onchain fees') + ': ' + app.fee_status
description: _('Choose how transaction fees are estimated')
action: lambda dt: app.fee_dialog()
CardSeparator
SettingsItem: SettingsItem:
status: root.fx_status() status: root.fx_status()
title: _('Fiat Currency') + ': ' + self.status title: _('Fiat Currency') + ': ' + self.status
@ -66,16 +61,6 @@ Builder.load_string('''
description: _("Save and synchronize your labels.") description: _("Save and synchronize your labels.")
action: partial(root.plugin_dialog, 'labels', self) action: partial(root.plugin_dialog, 'labels', self)
CardSeparator 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: SettingsItem:
status: _('Yes') if app.use_unconfirmed else _('No') status: _('Yes') if app.use_unconfirmed else _('No')
title: _('Spend unconfirmed') + ': ' + self.status title: _('Spend unconfirmed') + ': ' + self.status

10
electrum/gui/kivy/uix/screens.py

@ -36,7 +36,7 @@ from electrum.lnutil import RECEIVED, SENT, PaymentFailure
from electrum.logging import Logger from electrum.logging import Logger
from .dialogs.question import Question 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 import KIVY_GUI_PATH
from electrum.gui.kivy.i18n import _ from electrum.gui.kivy.i18n import _
@ -372,8 +372,12 @@ class SendScreen(CScreen, Logger):
threading.Thread(target=pay_thread).start() threading.Thread(target=pay_thread).start()
def _do_pay_onchain(self, invoice: OnchainInvoice) -> None: def _do_pay_onchain(self, invoice: OnchainInvoice) -> None:
from .dialogs.confirm_tx_dialog import ConfirmTxDialog outputs = invoice.outputs
d = ConfirmTxDialog(self.app, invoice) 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() d.open()
def send_tx(self, tx, invoice, password): def send_tx(self, tx, invoice, password):

18
electrum/gui/qt/main_window.py

@ -1796,9 +1796,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
def mktx_for_open_channel(self, funding_sat): def mktx_for_open_channel(self, funding_sat):
coins = self.get_coins(nonlocal_only=True) coins = self.get_coins(nonlocal_only=True)
make_tx = lambda fee_est: self.wallet.lnworker.mktx_for_open_channel(coins=coins, make_tx = lambda fee_est: self.wallet.lnworker.mktx_for_open_channel(
funding_sat=funding_sat, coins=coins,
fee_est=fee_est) funding_sat=funding_sat,
fee_est=fee_est)
return make_tx return make_tx
def open_channel(self, connect_str, funding_sat, push_amt): 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 # read funding_sat from tx; converts '!' to int value
funding_sat = funding_tx.output_value_for_address(ln_dummy_address()) funding_sat = funding_tx.output_value_for_address(ln_dummy_address())
def task(): def task():
return self.wallet.lnworker.open_channel(connect_str=connect_str, return self.wallet.lnworker.open_channel(
funding_tx=funding_tx, connect_str=connect_str,
funding_sat=funding_sat, funding_tx=funding_tx,
push_amt_sat=push_amt, funding_sat=funding_sat,
password=password) push_amt_sat=push_amt,
password=password)
def on_success(args): def on_success(args):
chan, funding_tx = args chan, funding_tx = args
n = chan.constraints.funding_txn_minimum_depth n = chan.constraints.funding_txn_minimum_depth

34
electrum/wallet.py

@ -183,8 +183,8 @@ async def sweep(
fee: int = None, fee: int = None,
imax=100, imax=100,
locktime=None, locktime=None,
tx_version=None tx_version=None) -> PartialTransaction:
) -> PartialTransaction:
inputs, keypairs = await sweep_preparations(privkeys, network, imax) inputs, keypairs = await sweep_preparations(privkeys, network, imax)
total = sum(txin.value_sats() for txin in inputs) total = sum(txin.value_sats() for txin in inputs)
if fee is None: 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}" assert is_address(selected_addr), f"not valid bitcoin address: {selected_addr}"
return selected_addr return selected_addr
def make_unsigned_transaction(self, *, coins: Sequence[PartialTxInput], def make_unsigned_transaction(
outputs: List[PartialTxOutput], fee=None, self, *,
change_addr: str = None, is_sweep=False) -> PartialTransaction: 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]): if any([c.already_has_some_signatures() for c in coins]):
raise Exception("Some inputs already contain signatures!") raise Exception("Some inputs already contain signatures!")
@ -1298,12 +1303,13 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
old_change_addrs = [] old_change_addrs = []
# change address. if empty, coin_chooser will set it # change address. if empty, coin_chooser will set it
change_addrs = self.get_change_addresses_for_new_transaction(change_addr or old_change_addrs) change_addrs = self.get_change_addresses_for_new_transaction(change_addr or old_change_addrs)
tx = coin_chooser.make_tx(coins=coins, tx = coin_chooser.make_tx(
inputs=txi, coins=coins,
outputs=list(outputs) + txo, inputs=txi,
change_addrs=change_addrs, outputs=list(outputs) + txo,
fee_estimator_vb=fee_estimator, change_addrs=change_addrs,
dust_threshold=self.dust_threshold()) fee_estimator_vb=fee_estimator,
dust_threshold=self.dust_threshold())
else: else:
# "spend max" branch # "spend max" branch
# note: This *will* spend inputs with negative effective value (if there are any). # 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. # Timelock tx to current height.
tx.locktime = get_locktime_for_new_transaction(self.network) 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) tx.add_info_from_wallet(self)
run_hook('make_unsigned_transaction', self, tx) run_hook('make_unsigned_transaction', self, tx)
return tx return tx
@ -1340,8 +1346,8 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
coins=coins, coins=coins,
outputs=outputs, outputs=outputs,
fee=fee, fee=fee,
change_addr=change_addr) change_addr=change_addr,
tx.set_rbf(rbf) rbf=rbf)
if tx_version is not None: if tx_version is not None:
tx.version = tx_version tx.version = tx_version
if sign: if sign:

Loading…
Cancel
Save