Browse Source

Add 'channels' parameter to create invoice and pay.

Add rebalance dialog to GUI
patch-4
ThomasV 3 years ago
parent
commit
bc9cc51800
  1. 14
      electrum/gui/qt/channels_list.py
  2. 40
      electrum/gui/qt/main_window.py
  3. 47
      electrum/lnworker.py

14
electrum/gui/qt/channels_list.py

@ -202,8 +202,18 @@ class ChannelsList(MyTreeView):
menu.addAction(_("Import channel backup"), lambda: self.parent.do_process_from_text_channel_backup())
menu.exec_(self.viewport().mapToGlobal(position))
return
multi_select = len(selected) > 1
if multi_select:
if len(selected) == 2:
idx1 = selected[0]
idx2 = selected[1]
channel_id1 = idx1.sibling(idx1.row(), self.Columns.NODE_ALIAS).data(ROLE_CHANNEL_ID)
channel_id2 = idx2.sibling(idx2.row(), self.Columns.NODE_ALIAS).data(ROLE_CHANNEL_ID)
chan1 = self.lnworker.channels.get(channel_id1)
chan2 = self.lnworker.channels.get(channel_id2)
if chan1 and chan2:
menu.addAction(_("Rebalance"), lambda: self.parent.rebalance_dialog(chan1, chan2))
menu.exec_(self.viewport().mapToGlobal(position))
return
elif len(selected) > 2:
return
idx = self.indexAt(position)
if not idx.isValid():

40
electrum/gui/qt/main_window.py

@ -3652,3 +3652,43 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
_("Electrum will now exit."))
self.showing_cert_mismatch_error = False
self.close()
def rebalance_dialog(self, chan1, chan2, amount_sat=None):
d = WindowModalDialog(self, _("Rebalance channels"))
d.reverse = False
vbox = QVBoxLayout(d)
vbox.addWidget(WWLabel(_('Rebalance your channels in order to increase your sending or receiving capacity') + ':'))
grid = QGridLayout()
amount_e = BTCAmountEdit(self.get_decimal_point)
amount_e.setAmount(amount_sat)
rev_button = QPushButton(u'\U000021c4')
label1 = QLabel('')
label2 = QLabel('')
def update():
if d.reverse:
d.chan_from = chan2
d.chan_to = chan1
else:
d.chan_from = chan1
d.chan_to = chan2
label1.setText(d.chan_from.short_id_for_GUI())
label2.setText(d.chan_to.short_id_for_GUI())
update()
def on_reverse():
d.reverse = not d.reverse
update()
rev_button.clicked.connect(on_reverse)
grid.addWidget(QLabel(_("From")), 0, 0)
grid.addWidget(label1, 0, 1)
grid.addWidget(QLabel(_("To")), 1, 0)
grid.addWidget(label2, 1, 1)
grid.addWidget(QLabel(_("Amount")), 2, 0)
grid.addWidget(amount_e, 2, 1)
grid.addWidget(rev_button, 0, 2)
vbox.addLayout(grid)
vbox.addLayout(Buttons(CancelButton(d), OkButton(d)))
if not d.exec_():
return
amount_msat = amount_e.get_amount() * 1000
coro = self.wallet.lnworker.rebalance_channels(d.chan_from, d.chan_to, amount_msat=amount_msat)
self.run_coroutine_from_thread(coro)

47
electrum/lnworker.py

@ -1103,7 +1103,9 @@ class LNWallet(LNWorker):
self, invoice: str, *,
amount_msat: int = None,
attempts: int = None, # used only in unit tests
full_path: LNPaymentPath = None) -> Tuple[bool, List[HtlcLog]]:
full_path: LNPaymentPath = None,
channels: Optional[Sequence[Channel]] = None,
) -> Tuple[bool, List[HtlcLog]]:
lnaddr = self._check_invoice(invoice, amount_msat=amount_msat)
min_cltv_expiry = lnaddr.get_min_final_cltv_expiry()
@ -1138,7 +1140,8 @@ class LNWallet(LNWorker):
r_tags=r_tags,
invoice_features=invoice_features,
attempts=attempts,
full_path=full_path)
full_path=full_path,
channels=channels)
success = True
except PaymentFailure as e:
self.logger.info(f'payment failure: {e!r}')
@ -1167,7 +1170,9 @@ class LNWallet(LNWorker):
full_path: LNPaymentPath = None,
fwd_trampoline_onion=None,
fwd_trampoline_fee=None,
fwd_trampoline_cltv_delta=None) -> None:
fwd_trampoline_cltv_delta=None,
channels: Optional[Sequence[Channel]] = None,
) -> None:
if fwd_trampoline_onion:
# todo: compare to the fee of the actual route we found
@ -1204,7 +1209,8 @@ class LNWallet(LNWorker):
payment_secret=payment_secret,
trampoline_fee_level=trampoline_fee_level,
use_two_trampolines=use_two_trampolines,
fwd_trampoline_onion=fwd_trampoline_onion
fwd_trampoline_onion=fwd_trampoline_onion,
channels=channels,
)
# 2. send htlcs
async for route, amount_msat, total_msat, amount_receiver_msat, cltv_delta, bucket_payment_secret, trampoline_onion in routes:
@ -1493,7 +1499,9 @@ class LNWallet(LNWorker):
trampoline_fee_level: int,
use_two_trampolines: bool,
fwd_trampoline_onion=None,
full_path: LNPaymentPath = None) -> AsyncGenerator[Tuple[LNPaymentRoute, int], None]:
full_path: LNPaymentPath = None,
channels: Optional[Sequence[Channel]] = None,
) -> AsyncGenerator[Tuple[LNPaymentRoute, int], None]:
"""Creates multiple routes for splitting a payment over the available
private channels.
@ -1503,8 +1511,12 @@ class LNWallet(LNWorker):
invoice_features = LnFeatures(invoice_features)
trampoline_features = LnFeatures.VAR_ONION_OPT
local_height = self.network.get_local_height()
my_active_channels = [chan for chan in self.channels.values() if
chan.is_active() and not chan.is_frozen_for_sending()]
if channels:
my_active_channels = channels
else:
my_active_channels = [
chan for chan in self.channels.values() if
chan.is_active() and not chan.is_frozen_for_sending()]
try:
self.logger.info("trying single-part payment")
# try to send over a single channel
@ -1760,11 +1772,12 @@ class LNWallet(LNWorker):
expiry: int,
fallback_address: str,
write_to_disk: bool = True,
channels: Optional[Sequence[Channel]] = None,
) -> Tuple[LnAddr, str]:
assert amount_msat is None or amount_msat > 0
timestamp = int(time.time())
routing_hints, trampoline_hints = self.calc_routing_hints_for_invoice(amount_msat)
routing_hints, trampoline_hints = self.calc_routing_hints_for_invoice(amount_msat, channels=channels)
if not routing_hints:
self.logger.info(
"Warning. No routing hints added to invoice. "
@ -1993,11 +2006,12 @@ class LNWallet(LNWorker):
self.set_invoice_status(key, PR_UNPAID)
util.trigger_callback('payment_failed', self.wallet, key, '')
def calc_routing_hints_for_invoice(self, amount_msat: Optional[int]):
def calc_routing_hints_for_invoice(self, amount_msat: Optional[int], channels=None):
"""calculate routing hints (BOLT-11 'r' field)"""
routing_hints = []
channels = list(self.get_channels_to_include_in_invoice(amount_msat))
random.shuffle(channels) # let's not leak channel order
if channels is None:
channels = list(self.get_channels_to_include_in_invoice(amount_msat))
random.shuffle(channels) # let's not leak channel order
scid_to_my_channels = {chan.short_channel_id: chan for chan in channels
if chan.short_channel_id is not None}
for chan in channels:
@ -2119,6 +2133,17 @@ class LNWallet(LNWorker):
)
return Decimal(can_receive_msat) / 1000
async def rebalance_channels(self, chan1, chan2, amount_msat):
lnaddr, invoice = self.create_invoice(
amount_msat=amount_msat,
message='rebalance',
expiry=3600,
fallback_address=None,
channels = [chan2]
)
await self.pay_invoice(
invoice, channels=[chan1])
def num_sats_can_receive_no_mpp(self) -> Decimal:
with self.lock:
channels = [

Loading…
Cancel
Save