mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-23 17:47:31 +00:00
channel details with list of htlcs
This commit is contained in:
parent
9d32031ca2
commit
e3409d32ef
10 changed files with 195 additions and 28 deletions
|
@ -294,7 +294,7 @@ class SendScreen(CScreen):
|
|||
fut = asyncio.run_coroutine_threadsafe(coro, self.app.network.asyncio_loop)
|
||||
fut.add_done_callback(self.ln_payment_result)
|
||||
|
||||
def payment_completed_async_thread(self, event, direction, htlc, preimage):
|
||||
def payment_completed_async_thread(self, event, date, direction, htlc, preimage, chan_id):
|
||||
Clock.schedule_once(lambda dt: self.payment_completed(direction, htlc, preimage))
|
||||
|
||||
def payment_completed(self, direction, htlc, preimage):
|
||||
|
|
141
electrum/gui/qt/channel_details.py
Normal file
141
electrum/gui/qt/channel_details.py
Normal file
|
@ -0,0 +1,141 @@
|
|||
from typing import Optional, TYPE_CHECKING
|
||||
|
||||
import PyQt5.QtGui as QtGui
|
||||
import PyQt5.QtWidgets as QtWidgets
|
||||
import PyQt5.QtCore as QtCore
|
||||
|
||||
from electrum.i18n import _
|
||||
from electrum.lnchan import UpdateAddHtlc
|
||||
from electrum.util import bh2u, format_time
|
||||
from electrum.lnchan import HTLCOwner
|
||||
from electrum.lnaddr import LnAddr, lndecode
|
||||
if TYPE_CHECKING:
|
||||
from .main_window import ElectrumWindow
|
||||
|
||||
class HTLCItem(QtGui.QStandardItem):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.setEditable(False)
|
||||
|
||||
class ChannelDetailsDialog(QtWidgets.QDialog):
|
||||
|
||||
def make_inflight(self, lnaddr, i: UpdateAddHtlc):
|
||||
it = HTLCItem(_('HTLC with ID ') + str(i.htlc_id))
|
||||
it.appendRow([HTLCItem(_('Amount')),HTLCItem(self.format(i.amount_msat))])
|
||||
it.appendRow([HTLCItem(_('CLTV expiry')),HTLCItem(str(i.cltv_expiry))])
|
||||
it.appendRow([HTLCItem(_('Payment hash')),HTLCItem(bh2u(i.payment_hash))])
|
||||
invoice = HTLCItem(_('Invoice'))
|
||||
invoice.appendRow([HTLCItem(_('Remote node public key')), HTLCItem(bh2u(lnaddr.pubkey.serialize()))])
|
||||
invoice.appendRow([HTLCItem(_('Amount in BTC')), HTLCItem(str(lnaddr.amount))])
|
||||
invoice.appendRow([HTLCItem(_('Description')), HTLCItem(dict(lnaddr.tags).get('d', _('N/A')))])
|
||||
invoice.appendRow([HTLCItem(_('Date')), HTLCItem(format_time(lnaddr.date))])
|
||||
it.appendRow([invoice])
|
||||
return it
|
||||
|
||||
def make_model(self, htlcs):
|
||||
model = QtGui.QStandardItemModel(0, 2)
|
||||
model.setHorizontalHeaderLabels(['HTLC', 'Property value'])
|
||||
parentItem = model.invisibleRootItem()
|
||||
folder_types = {'settled': _('Fulfilled HTLCs'), 'inflight': _('HTLCs in current commitment transaction')}
|
||||
self.folders = {}
|
||||
|
||||
self.keyname_rows = {}
|
||||
|
||||
for keyname, i in folder_types.items():
|
||||
myFont=QtGui.QFont()
|
||||
myFont.setBold(True)
|
||||
folder = HTLCItem(i)
|
||||
folder.setFont(myFont)
|
||||
parentItem.appendRow(folder)
|
||||
self.folders[keyname] = folder
|
||||
mapping = {}
|
||||
num = 0
|
||||
if keyname == 'inflight':
|
||||
for lnaddr, i in htlcs[keyname]:
|
||||
it = self.make_inflight(lnaddr, i)
|
||||
self.folders[keyname].appendRow(it)
|
||||
mapping[i.payment_hash] = num
|
||||
num += 1
|
||||
elif keyname == 'settled':
|
||||
for date, direction, i, preimage in htlcs[keyname]:
|
||||
it = HTLCItem(_('HTLC with ID ') + str(i.htlc_id))
|
||||
it.appendRow([HTLCItem(_('Amount')),HTLCItem(self.format(i.amount_msat))])
|
||||
it.appendRow([HTLCItem(_('CLTV expiry')),HTLCItem(str(i.cltv_expiry))])
|
||||
it.appendRow([HTLCItem(_('Payment hash')),HTLCItem(bh2u(i.payment_hash))])
|
||||
# NOTE no invoices because user can delete invoices after settlement
|
||||
self.folders[keyname].appendRow(it)
|
||||
mapping[i.payment_hash] = num
|
||||
num += 1
|
||||
|
||||
self.keyname_rows[keyname] = mapping
|
||||
return model
|
||||
|
||||
def move(self, fro: str, to: str, payment_hash: bytes):
|
||||
assert fro != to
|
||||
row_idx = self.keyname_rows[fro].pop(payment_hash)
|
||||
row = self.folders[fro].takeRow(row_idx)
|
||||
self.folders[to].appendRow(row)
|
||||
dest_mapping = self.keyname_rows[to]
|
||||
dest_mapping[payment_hash] = len(dest_mapping)
|
||||
|
||||
ln_payment_completed = QtCore.pyqtSignal(str, float, HTLCOwner, UpdateAddHtlc, bytes, bytes)
|
||||
htlc_added = QtCore.pyqtSignal(str, UpdateAddHtlc, LnAddr, HTLCOwner)
|
||||
|
||||
@QtCore.pyqtSlot(str, UpdateAddHtlc, LnAddr, HTLCOwner)
|
||||
def do_htlc_added(self, evtname, htlc, lnaddr, direction):
|
||||
mapping = self.keyname_rows['inflight']
|
||||
mapping[htlc.payment_hash] = len(mapping)
|
||||
self.folders['inflight'].appendRow(self.make_inflight(lnaddr, htlc))
|
||||
|
||||
@QtCore.pyqtSlot(str, float, HTLCOwner, UpdateAddHtlc, bytes, bytes)
|
||||
def do_ln_payment_completed(self, evtname, date, direction, htlc, preimage, chan_id):
|
||||
self.move('inflight', 'settled', htlc.payment_hash)
|
||||
|
||||
def __init__(self, window: Optional['ElectrumWindow'], chan_id: bytes):
|
||||
super().__init__(window)
|
||||
self.window = window
|
||||
assert type(window).__name__ in ['NoneType', 'ElectrumWindow']
|
||||
self.ln_payment_completed.connect(self.do_ln_payment_completed)
|
||||
self.htlc_added.connect(self.do_htlc_added)
|
||||
if not window:
|
||||
self.format = str
|
||||
htlcs = {
|
||||
'settled':
|
||||
[
|
||||
],
|
||||
'inflight':
|
||||
[
|
||||
(lndecode("lnbcrt100n1pdl9c2vpp5z6ztyjy8an80te3u6l0fxuhjzt9pfa27a27uqap3xt8nv6dq47esdqgw3jhxapncqzy3rzjq2j0zgr9slpsefhaem0rq9w3kgjx6mjfd9tp7pe8yw23jqydcdtrsqqrc5qqqqgqqqqqqqlgqqqqqqgqjq5v97p0f0ftkwzmpxhjj6magd5ars465krljcp5z28j3nxl8d0kqjkzf6acjerxdu3yvtus75kakx3yvyus6c68hdwm2hpunusr47w3gpee4hgp"), UpdateAddHtlc(amount_msat=10001, payment_hash=b"\x01"*32, cltv_expiry=500, htlc_id=1)),
|
||||
(lndecode('lnbcrt22m1pdl9kc7pp5qw903tar0e3ar4mu4h8m3zratj0sddqhfftpsjgcx0jsekzk43dsdqqcqzy3a6ev4vh6lt62xrzlq5l23g59pv0g3tur6drnduhczqg8smqlm75nklwx8r0mm535e4x8uq6tzqw7j7tvy70qaapfnt3e9n6rltvcs7cppzmqys'), UpdateAddHtlc(amount_msat=10002, payment_hash=b"\x02"*32, cltv_expiry=501, htlc_id=2)),
|
||||
(lndecode('lnbcrt1u1pdl9k6tpp58la47qfxz6mvtgjmnmkl8xe8vcrkhluxrldlhv3dgdlla6tr3mvqdqgw3jhxapncqzy3rzjq2j0zgr9slpsefhaem0rq9w3kgjx6mjfd9tp7pe8yw23jqydcdtrsqqrc5qqqqgqqqqqqqlgqqqqqqgqjqavsdk9qdjwgfdywhlqtuzn5atkhzt9sgjz6tfll67wc34rh80mqzjme3meqyutrj0p7tvxczeuag956h6fv0356ezstgpfgqy47d7vsq7vhx6l'), UpdateAddHtlc(amount_msat=10003, payment_hash=b"\x03"*32, cltv_expiry=502, htlc_id=3)),
|
||||
],
|
||||
}
|
||||
else:
|
||||
htlcs = self.window.wallet.lnworker._list_invoices(chan_id)
|
||||
self.format = lambda msat: self.window.format_amount_and_units(msat / 1000)
|
||||
self.window.network.register_callback(self.ln_payment_completed.emit, ['ln_payment_completed'])
|
||||
self.window.network.register_callback(self.htlc_added.emit, ['htlc_added'])
|
||||
self.setWindowTitle(_('Channel Details'))
|
||||
self.setMinimumSize(800, 400)
|
||||
vbox = QtWidgets.QVBoxLayout(self)
|
||||
w = QtWidgets.QTreeView(self)
|
||||
w.setModel(self.make_model(htlcs))
|
||||
#w.header().setStretchLastSection(False)
|
||||
w.header().setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents)
|
||||
vbox.addWidget(w)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
d = ChannelDetailsDialog(None, b"\x01"*32)
|
||||
d.show()
|
||||
|
||||
timer = QtCore.QTimer()
|
||||
timer.setSingleShot(True)
|
||||
def tick():
|
||||
d.move('inflight', 'settled', b'\x02' * 32)
|
||||
timer.timeout.connect(tick)
|
||||
timer.start(3000)
|
||||
|
||||
sys.exit(app.exec_())
|
|
@ -11,6 +11,7 @@ from electrum.lnutil import LOCAL, REMOTE, ConnStringFormatError
|
|||
|
||||
from .util import MyTreeWidget, SortableTreeWidgetItem, WindowModalDialog, Buttons, OkButton, CancelButton
|
||||
from .amountedit import BTCAmountEdit
|
||||
from .channel_details import ChannelDetailsDialog
|
||||
|
||||
class ChannelsList(MyTreeWidget):
|
||||
update_rows = QtCore.pyqtSignal()
|
||||
|
@ -62,10 +63,15 @@ class ChannelsList(MyTreeWidget):
|
|||
coro = lnworker.force_close_channel(channel_id)
|
||||
return network.run_from_another_thread(coro)
|
||||
WaitingDialog(self, 'please wait..', task, on_success, on_failure)
|
||||
menu.addAction(_("Details..."), lambda: self.details(channel_id))
|
||||
menu.addAction(_("Close channel"), close)
|
||||
menu.addAction(_("Force-close channel"), force_close)
|
||||
menu.exec_(self.viewport().mapToGlobal(position))
|
||||
|
||||
def details(self, channel_id):
|
||||
assert self.parent.wallet
|
||||
ChannelDetailsDialog(self.parent, channel_id).show()
|
||||
|
||||
@QtCore.pyqtSlot(Channel)
|
||||
def do_update_single_row(self, chan):
|
||||
for i in range(self.topLevelItemCount()):
|
||||
|
|
|
@ -396,7 +396,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
|
|||
self.require_fee_update = True
|
||||
self.history_model.on_fee_histogram()
|
||||
elif event == 'ln_message':
|
||||
lnworker, message = args
|
||||
lnworker, message, htlc_id = args
|
||||
if lnworker == self.wallet.lnworker:
|
||||
self.show_message(message)
|
||||
else:
|
||||
|
|
|
@ -250,7 +250,7 @@ class LnAddr(object):
|
|||
|
||||
def __str__(self):
|
||||
return "LnAddr[{}, amount={}{} tags=[{}]]".format(
|
||||
hexlify(self.pubkey.serialize()).decode('utf-8'),
|
||||
hexlify(self.pubkey.serialize()).decode('utf-8') if self.pubkey else None,
|
||||
self.amount, self.currency,
|
||||
", ".join([k + '=' + str(v) for k, v in self.tags])
|
||||
)
|
||||
|
|
|
@ -25,8 +25,8 @@ from .util import PrintError, bh2u, print_error, bfh, log_exceptions, list_enabl
|
|||
from .transaction import Transaction, TxOutput
|
||||
from .lnonion import (new_onion_packet, decode_onion_error, OnionFailureCode, calc_hops_data_for_payment,
|
||||
process_onion_packet, OnionPacket, construct_onion_error, OnionRoutingFailureMessage)
|
||||
from .lnchan import Channel, RevokeAndAck, htlcsum
|
||||
from .lnutil import (Outpoint, LocalConfig,
|
||||
from .lnchan import Channel, RevokeAndAck, htlcsum, UpdateAddHtlc
|
||||
from .lnutil import (Outpoint, LocalConfig, RECEIVED,
|
||||
RemoteConfig, OnlyPubkeyKeypair, ChannelConstraints, RevocationStore,
|
||||
funding_output_script, get_per_commitment_secret_from_seed,
|
||||
secret_to_pubkey, LNPeerAddr, PaymentFailure, LnLocalFeatures,
|
||||
|
@ -896,7 +896,7 @@ class Peer(PrintError):
|
|||
self.revoke(chan)
|
||||
self.send_commitment(chan) # htlc will be removed
|
||||
await self.receive_revoke(chan)
|
||||
self.network.trigger_callback('ln_message', self.lnworker, 'Payment failed')
|
||||
self.network.trigger_callback('ln_message', self.lnworker, 'Payment failed', htlc_id)
|
||||
|
||||
async def _handle_error_code_from_failed_htlc(self, error_reason, route: List['RouteEdge'], channel_id, htlc_id):
|
||||
chan = self.channels[channel_id]
|
||||
|
@ -969,6 +969,7 @@ class Peer(PrintError):
|
|||
self.attempted_route[(chan.channel_id, htlc_id)] = route
|
||||
self.print_error(f"starting payment. route: {route}")
|
||||
await self.update_channel(chan, "update_add_htlc", channel_id=chan.channel_id, id=htlc_id, cltv_expiry=cltv, amount_msat=amount_msat, payment_hash=payment_hash, onion_routing_packet=onion.to_bytes())
|
||||
return UpdateAddHtlc(**htlc, htlc_id=htlc_id)
|
||||
|
||||
async def receive_revoke(self, chan: Channel):
|
||||
revoke_and_ack_msg = await self.revoke_and_ack[chan.channel_id].get()
|
||||
|
@ -1007,7 +1008,7 @@ class Peer(PrintError):
|
|||
self.revoke(chan)
|
||||
self.send_commitment(chan) # htlc will be removed
|
||||
await self.receive_revoke(chan)
|
||||
self.network.trigger_callback('ln_message', self.lnworker, 'Payment sent')
|
||||
self.network.trigger_callback('ln_message', self.lnworker, 'Payment sent', htlc_id)
|
||||
|
||||
# used in lightning-integration
|
||||
self.payment_preimages[sha256(preimage)].put_nowait(preimage)
|
||||
|
@ -1034,7 +1035,7 @@ class Peer(PrintError):
|
|||
pass # TODO fail the channel
|
||||
# add htlc
|
||||
htlc = {'amount_msat': amount_msat_htlc, 'payment_hash':payment_hash, 'cltv_expiry':cltv_expiry}
|
||||
chan.receive_htlc(htlc)
|
||||
htlc_id = chan.receive_htlc(htlc)
|
||||
await self.receive_commitment(chan)
|
||||
self.revoke(chan)
|
||||
self.send_commitment(chan)
|
||||
|
@ -1074,6 +1075,7 @@ class Peer(PrintError):
|
|||
data=amount_msat_htlc.to_bytes(8, byteorder="big"))
|
||||
await self.fail_htlc(chan, htlc_id, onion_packet, reason)
|
||||
return
|
||||
self.network.trigger_callback('htlc_added', UpdateAddHtlc(**htlc, htlc_id=htlc_id), invoice, RECEIVED)
|
||||
# settle htlc
|
||||
await self.settle_htlc(chan, htlc_id, preimage)
|
||||
|
||||
|
@ -1083,7 +1085,7 @@ class Peer(PrintError):
|
|||
channel_id=chan.channel_id,
|
||||
id=htlc_id,
|
||||
payment_preimage=preimage)
|
||||
self.network.trigger_callback('ln_message', self.lnworker, 'Payment received')
|
||||
self.network.trigger_callback('ln_message', self.lnworker, 'Payment received', htlc_id)
|
||||
|
||||
async def fail_htlc(self, chan: Channel, htlc_id: int, onion_packet: OnionPacket,
|
||||
reason: OnionRoutingFailureMessage):
|
||||
|
|
|
@ -149,7 +149,7 @@ class Channel(PrintError):
|
|||
def __init__(self, state, name = None, payment_completed : Optional[Callable[[HTLCOwner, UpdateAddHtlc, bytes], None]] = None):
|
||||
self.preimages = {}
|
||||
if not payment_completed:
|
||||
payment_completed = lambda x, y, z: None
|
||||
payment_completed = lambda this, x, y, z: None
|
||||
self.payment_completed = payment_completed
|
||||
assert 'local_state' not in state
|
||||
self.config = {}
|
||||
|
@ -505,7 +505,7 @@ class Channel(PrintError):
|
|||
preimage = self.preimages.pop(htlc_id)
|
||||
else:
|
||||
preimage = None
|
||||
self.payment_completed(subject, htlc, preimage)
|
||||
self.payment_completed(self, subject, htlc, preimage)
|
||||
self.log[subject].settles.clear()
|
||||
|
||||
return old_amount - htlcsum(self.htlcs(subject, False))
|
||||
|
|
|
@ -595,7 +595,7 @@ def extract_nodeid(connect_contents: str) -> Tuple[bytes, str]:
|
|||
raise ConnStringFormatError(_('At least a hostname must be supplied after the at symbol.'))
|
||||
try:
|
||||
node_id = bfh(nodeid_hex)
|
||||
assert len(node_id) == 33
|
||||
assert len(node_id) == 33, len(node_id)
|
||||
except:
|
||||
raise ConnStringFormatError(_('Invalid node ID, must be 33 bytes and hexadecimal'))
|
||||
return node_id, rest
|
||||
|
|
|
@ -12,6 +12,7 @@ import threading
|
|||
import socket
|
||||
import json
|
||||
from datetime import datetime, timezone
|
||||
from functools import partial
|
||||
|
||||
import dns.resolver
|
||||
import dns.exception
|
||||
|
@ -62,7 +63,7 @@ class LNWorker(PrintError):
|
|||
def __init__(self, wallet: 'Abstract_Wallet', network: 'Network'):
|
||||
self.wallet = wallet
|
||||
# invoices we are currently trying to pay (might be pending HTLCs on a commitment transaction)
|
||||
self.paying = self.wallet.storage.get('lightning_payments_inflight', {}) # type: Dict[bytes, Tuple[str, Optional[int]]]
|
||||
self.paying = self.wallet.storage.get('lightning_payments_inflight', {}) # type: Dict[bytes, Tuple[str, Optional[int], bytes]]
|
||||
self.sweep_address = wallet.get_receiving_address()
|
||||
self.network = network
|
||||
self.channel_db = self.network.channel_db
|
||||
|
@ -71,9 +72,11 @@ class LNWorker(PrintError):
|
|||
self.node_keypair = generate_keypair(self.ln_keystore, LnKeyFamily.NODE_KEY, 0)
|
||||
self.config = network.config
|
||||
self.peers = {} # type: Dict[bytes, Peer] # pubkey -> Peer
|
||||
channels_map = map(lambda x: Channel(x, payment_completed=self.payment_completed), wallet.storage.get("channels", []))
|
||||
self.channels = {x.channel_id: x for x in channels_map} # type: Dict[bytes, Channel]
|
||||
for c in self.channels.values():
|
||||
self.channels = {} # type: Dict[bytes, Channel]
|
||||
for x in wallet.storage.get("channels", []):
|
||||
c = Channel(x, payment_completed=self.payment_completed)
|
||||
self.channels[c.channel_id] = c
|
||||
|
||||
c.lnwatcher = network.lnwatcher
|
||||
c.sweep_address = self.sweep_address
|
||||
self.invoices = wallet.storage.get('lightning_invoices', {}) # type: Dict[str, Tuple[str,str]] # RHASH -> (preimage, invoice)
|
||||
|
@ -86,7 +89,8 @@ class LNWorker(PrintError):
|
|||
self.network.register_callback(self.on_channel_txo, ['channel_txo'])
|
||||
asyncio.run_coroutine_threadsafe(self.network.main_taskgroup.spawn(self.main_loop()), self.network.asyncio_loop)
|
||||
|
||||
def payment_completed(self, direction, htlc, preimage):
|
||||
def payment_completed(self, chan, direction, htlc, preimage):
|
||||
chan_id = chan.channel_id
|
||||
if direction == SENT:
|
||||
assert htlc.payment_hash not in self.invoices
|
||||
self.paying.pop(bh2u(htlc.payment_hash))
|
||||
|
@ -94,10 +98,11 @@ class LNWorker(PrintError):
|
|||
l = self.wallet.storage.get('lightning_payments_completed', [])
|
||||
if not preimage:
|
||||
preimage, _addr = self.get_invoice(htlc.payment_hash)
|
||||
l.append((time.time(), direction, json.loads(encoder.encode(htlc)), bh2u(preimage)))
|
||||
tupl = (time.time(), direction, json.loads(encoder.encode(htlc)), bh2u(preimage), bh2u(chan_id))
|
||||
l.append(tupl)
|
||||
self.wallet.storage.put('lightning_payments_completed', l)
|
||||
self.wallet.storage.write()
|
||||
self.network.trigger_callback('ln_payment_completed', direction, htlc, preimage)
|
||||
self.network.trigger_callback('ln_payment_completed', tupl[0], direction, htlc, preimage, chan_id)
|
||||
|
||||
def list_invoices(self):
|
||||
report = self._list_invoices()
|
||||
|
@ -128,13 +133,16 @@ class LNWorker(PrintError):
|
|||
yield str(htlc)
|
||||
yield ''
|
||||
|
||||
def _list_invoices(self):
|
||||
def _list_invoices(self, chan_id=None):
|
||||
invoices = dict(self.invoices)
|
||||
completed = self.wallet.storage.get('lightning_payments_completed', [])
|
||||
settled = []
|
||||
unsettled = []
|
||||
inflight = []
|
||||
for date, direction, htlc, hex_preimage in completed:
|
||||
for date, direction, htlc, hex_preimage, hex_chan_id in completed:
|
||||
if chan_id is not None:
|
||||
if bfh(hex_chan_id) != chan_id:
|
||||
continue
|
||||
htlcobj = UpdateAddHtlc(*htlc)
|
||||
if direction == RECEIVED:
|
||||
preimage = bfh(invoices.pop(bh2u(htlcobj.payment_hash))[0])
|
||||
|
@ -145,18 +153,21 @@ class LNWorker(PrintError):
|
|||
for preimage, pay_req in invoices.values():
|
||||
addr = lndecode(pay_req, expected_hrp=constants.net.SEGWIT_HRP)
|
||||
unsettled.append((addr, bfh(preimage), pay_req))
|
||||
for pay_req, amount_sat in self.paying.values():
|
||||
for pay_req, amount_sat, this_chan_id in self.paying.values():
|
||||
if chan_id is not None and this_chan_id != chan_id:
|
||||
continue
|
||||
addr = lndecode(pay_req, expected_hrp=constants.net.SEGWIT_HRP)
|
||||
if amount_sat is not None:
|
||||
addr.amount = Decimal(amount_sat) / COIN
|
||||
htlc = self.find_htlc_for_addr(addr)
|
||||
htlc = self.find_htlc_for_addr(addr, None if chan_id is None else [chan_id])
|
||||
if not htlc:
|
||||
self.print_error('Warning, in flight HTLC not found in any channel')
|
||||
inflight.append((addr, htlc))
|
||||
return {'settled': settled, 'unsettled': unsettled, 'inflight': inflight}
|
||||
|
||||
def find_htlc_for_addr(self, addr):
|
||||
for chan in self.channels.values():
|
||||
def find_htlc_for_addr(self, addr, whitelist=None):
|
||||
channels = [y for x,y in self.channels.items() if x in whitelist or whitelist is None]
|
||||
for chan in channels:
|
||||
for htlc in chan.log[LOCAL].adds.values():
|
||||
if htlc.payment_hash == addr.paymenthash:
|
||||
return htlc
|
||||
|
@ -362,7 +373,13 @@ class LNWorker(PrintError):
|
|||
addr = self._check_invoice(invoice, amount_sat)
|
||||
route = self._create_route_from_invoice(decoded_invoice=addr)
|
||||
peer = self.peers[route[0].node_id]
|
||||
self.paying[bh2u(addr.paymenthash)] = (invoice, amount_sat)
|
||||
for chan in self.channels.values():
|
||||
if chan.short_channel_id == route[0].short_channel_id:
|
||||
chan_id = chan.channel_id
|
||||
break
|
||||
else:
|
||||
assert False, 'Found route with short channel ID we don\'t have: ' + repr(route[0].short_channel_id)
|
||||
self.paying[bh2u(addr.paymenthash)] = (invoice, amount_sat, chan_id)
|
||||
self.wallet.storage.put('lightning_payments_inflight', self.paying)
|
||||
self.wallet.storage.write()
|
||||
return addr, peer, self._pay_to_route(route, addr)
|
||||
|
@ -377,7 +394,8 @@ class LNWorker(PrintError):
|
|||
else:
|
||||
raise Exception("PathFinder returned path with short_channel_id {} that is not in channel list".format(bh2u(short_channel_id)))
|
||||
peer = self.peers[route[0].node_id]
|
||||
return await peer.pay(route, chan, int(addr.amount * COIN * 1000), addr.paymenthash, addr.get_min_final_cltv_expiry())
|
||||
htlc = await peer.pay(route, chan, int(addr.amount * COIN * 1000), addr.paymenthash, addr.get_min_final_cltv_expiry())
|
||||
self.network.trigger_callback('htlc_added', htlc, addr, SENT)
|
||||
|
||||
@staticmethod
|
||||
def _check_invoice(invoice, amount_sat=None):
|
||||
|
|
|
@ -195,7 +195,7 @@ class TestPeer(unittest.TestCase):
|
|||
def prepare_ln_message_future(w2 # receiver
|
||||
):
|
||||
fut = asyncio.Future()
|
||||
def evt_set(event, _lnworker, msg):
|
||||
def evt_set(event, _lnworker, msg, _htlc_id):
|
||||
fut.set_result(msg)
|
||||
w2.network.register_callback(evt_set, ['ln_message'])
|
||||
return fut
|
||||
|
|
Loading…
Add table
Reference in a new issue