Add 'channels' parameter to create invoice and pay.
Add rebalance dialog to GUI
This commit is contained in:
@@ -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():
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
Reference in New Issue
Block a user