mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-09-08 11:39:53 +00:00
redo LNWorker pay:
- wait until htlc has been fulfilled - raise if htlc is not fulfilled - return boolean success - try multiple paths in GUI
This commit is contained in:
parent
669b84fbd6
commit
6d9ef29690
6 changed files with 24 additions and 42 deletions
|
@ -781,8 +781,7 @@ class Commands:
|
||||||
|
|
||||||
@command('wn')
|
@command('wn')
|
||||||
def lnpay(self, invoice):
|
def lnpay(self, invoice):
|
||||||
addr, peer, f = self.lnworker.pay(invoice)
|
return self.lnworker.pay(invoice, timeout=10)
|
||||||
return f.result()
|
|
||||||
|
|
||||||
@command('wn')
|
@command('wn')
|
||||||
def addinvoice(self, requested_amount, message):
|
def addinvoice(self, requested_amount, message):
|
||||||
|
|
|
@ -1675,33 +1675,25 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
|
||||||
|
|
||||||
def pay_lightning_invoice(self, invoice):
|
def pay_lightning_invoice(self, invoice):
|
||||||
amount = self.amount_e.get_amount()
|
amount = self.amount_e.get_amount()
|
||||||
LN_NUM_PAYMENT_ATTEMPTS = 1 # TODO increase
|
LN_NUM_PAYMENT_ATTEMPTS = 3
|
||||||
|
|
||||||
def on_success(result):
|
def on_success(result):
|
||||||
self.logger.info(f'ln payment success. {result}')
|
self.logger.info(f'ln payment success. {result}')
|
||||||
self.do_clear()
|
self.do_clear()
|
||||||
def on_failure(exc_info):
|
def on_failure(exc_info):
|
||||||
type_, e, traceback = exc_info
|
type_, e, traceback = exc_info
|
||||||
if isinstance(e, PaymentFailure):
|
if isinstance(e, PaymentFailure):
|
||||||
self.show_error(_('Payment failed. Tried {} times:\n{}')
|
self.show_error(_('Payment failed. {}').format(e))
|
||||||
.format(LN_NUM_PAYMENT_ATTEMPTS, e))
|
|
||||||
elif isinstance(e, InvoiceError):
|
elif isinstance(e, InvoiceError):
|
||||||
self.show_error(_('InvoiceError: {}').format(e))
|
self.show_error(_('InvoiceError: {}').format(e))
|
||||||
else:
|
else:
|
||||||
raise e
|
raise e
|
||||||
def task():
|
def task():
|
||||||
failure_list = []
|
|
||||||
for i in range(LN_NUM_PAYMENT_ATTEMPTS):
|
for i in range(LN_NUM_PAYMENT_ATTEMPTS):
|
||||||
try:
|
success = self.wallet.lnworker.pay(invoice, amount_sat=amount, timeout=30)
|
||||||
addr, peer, future = self.wallet.lnworker.pay(invoice, amount_sat=amount)
|
if success:
|
||||||
future.result()
|
|
||||||
break
|
break
|
||||||
except PaymentFailure as e:
|
|
||||||
failure_list.append(e)
|
|
||||||
# try again
|
|
||||||
else:
|
else:
|
||||||
msg = '\n'.join(str(e) for e in failure_list)
|
raise PaymentFailure('Failed after {i} attempts')
|
||||||
raise PaymentFailure(msg)
|
|
||||||
|
|
||||||
msg = _('Sending lightning payment...')
|
msg = _('Sending lightning payment...')
|
||||||
WaitingDialog(self, msg, task, on_success, on_failure)
|
WaitingDialog(self, msg, task, on_success, on_failure)
|
||||||
|
|
|
@ -973,7 +973,7 @@ class Peer(Logger):
|
||||||
@log_exceptions
|
@log_exceptions
|
||||||
async def _on_update_fail_htlc(self, chan, htlc_id, local_ctn):
|
async def _on_update_fail_htlc(self, chan, htlc_id, local_ctn):
|
||||||
await self.await_local(chan, local_ctn)
|
await self.await_local(chan, local_ctn)
|
||||||
self.network.trigger_callback('ln_message', self.lnworker, 'Payment failed', htlc_id)
|
self.lnworker.pending_payments[(chan.short_channel_id, htlc_id)].set_result(False)
|
||||||
|
|
||||||
def _handle_error_code_from_failed_htlc(self, error_reason, route: List['RouteEdge'], channel_id, htlc_id):
|
def _handle_error_code_from_failed_htlc(self, error_reason, route: List['RouteEdge'], channel_id, htlc_id):
|
||||||
chan = self.channels[channel_id]
|
chan = self.channels[channel_id]
|
||||||
|
@ -1117,7 +1117,7 @@ class Peer(Logger):
|
||||||
@log_exceptions
|
@log_exceptions
|
||||||
async def _on_update_fulfill_htlc(self, chan, htlc_id, preimage, local_ctn):
|
async def _on_update_fulfill_htlc(self, chan, htlc_id, preimage, local_ctn):
|
||||||
await self.await_local(chan, local_ctn)
|
await self.await_local(chan, local_ctn)
|
||||||
self.network.trigger_callback('ln_message', self.lnworker, 'Payment sent', htlc_id)
|
self.lnworker.pending_payments[(chan.short_channel_id, htlc_id)].set_result(True)
|
||||||
self.payment_preimages[sha256(preimage)].put_nowait(preimage)
|
self.payment_preimages[sha256(preimage)].put_nowait(preimage)
|
||||||
|
|
||||||
def on_update_fail_malformed_htlc(self, payload):
|
def on_update_fail_malformed_htlc(self, payload):
|
||||||
|
|
|
@ -314,6 +314,7 @@ class LNWallet(LNWorker):
|
||||||
c.set_local_commitment(c.current_commitment(LOCAL))
|
c.set_local_commitment(c.current_commitment(LOCAL))
|
||||||
# timestamps of opening and closing transactions
|
# timestamps of opening and closing transactions
|
||||||
self.channel_timestamps = self.storage.get('lightning_channel_timestamps', {})
|
self.channel_timestamps = self.storage.get('lightning_channel_timestamps', {})
|
||||||
|
self.pending_payments = defaultdict(asyncio.Future)
|
||||||
|
|
||||||
def start_network(self, network: 'Network'):
|
def start_network(self, network: 'Network'):
|
||||||
self.network = network
|
self.network = network
|
||||||
|
@ -641,14 +642,15 @@ class LNWallet(LNWorker):
|
||||||
chan = f.result(timeout)
|
chan = f.result(timeout)
|
||||||
return chan.funding_outpoint.to_str()
|
return chan.funding_outpoint.to_str()
|
||||||
|
|
||||||
def pay(self, invoice, amount_sat=None):
|
def pay(self, invoice, amount_sat=None, timeout=10):
|
||||||
"""
|
"""
|
||||||
This is not merged with _pay so that we can run the test with
|
Can be called from other threads
|
||||||
one thread only.
|
Raises timeout exception if htlc is not fulfilled
|
||||||
"""
|
"""
|
||||||
addr, peer, coro = self.network.run_from_another_thread(self._pay(invoice, amount_sat))
|
fut = asyncio.run_coroutine_threadsafe(
|
||||||
fut = asyncio.run_coroutine_threadsafe(coro, self.network.asyncio_loop)
|
self._pay(invoice, amount_sat),
|
||||||
return addr, peer, fut
|
self.network.asyncio_loop)
|
||||||
|
return fut.result(timeout=timeout)
|
||||||
|
|
||||||
def get_channel_by_short_id(self, short_channel_id):
|
def get_channel_by_short_id(self, short_channel_id):
|
||||||
with self.lock:
|
with self.lock:
|
||||||
|
@ -656,15 +658,14 @@ class LNWallet(LNWorker):
|
||||||
if chan.short_channel_id == short_channel_id:
|
if chan.short_channel_id == short_channel_id:
|
||||||
return chan
|
return chan
|
||||||
|
|
||||||
async def _pay(self, invoice, amount_sat=None, same_thread=False):
|
async def _pay(self, invoice, amount_sat=None):
|
||||||
addr = self._check_invoice(invoice, amount_sat)
|
addr = self._check_invoice(invoice, amount_sat)
|
||||||
self.save_invoice(addr.paymenthash, invoice, SENT, is_paid=False)
|
self.save_invoice(addr.paymenthash, invoice, SENT, is_paid=False)
|
||||||
self.wallet.set_label(bh2u(addr.paymenthash), addr.get_description())
|
self.wallet.set_label(bh2u(addr.paymenthash), addr.get_description())
|
||||||
route = await self._create_route_from_invoice(decoded_invoice=addr)
|
route = await self._create_route_from_invoice(decoded_invoice=addr)
|
||||||
peer = self.peers[route[0].node_id]
|
|
||||||
if not self.get_channel_by_short_id(route[0].short_channel_id):
|
if not self.get_channel_by_short_id(route[0].short_channel_id):
|
||||||
assert False, 'Found route with short channel ID we don\'t have: ' + repr(route[0].short_channel_id)
|
assert False, 'Found route with short channel ID we don\'t have: ' + repr(route[0].short_channel_id)
|
||||||
return addr, peer, self._pay_to_route(route, addr, invoice)
|
return await self._pay_to_route(route, addr, invoice)
|
||||||
|
|
||||||
async def _pay_to_route(self, route, addr, pay_req):
|
async def _pay_to_route(self, route, addr, pay_req):
|
||||||
short_channel_id = route[0].short_channel_id
|
short_channel_id = route[0].short_channel_id
|
||||||
|
@ -674,6 +675,8 @@ class LNWallet(LNWorker):
|
||||||
peer = self.peers[route[0].node_id]
|
peer = self.peers[route[0].node_id]
|
||||||
htlc = 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)
|
self.network.trigger_callback('htlc_added', htlc, addr, SENT)
|
||||||
|
success = await self.pending_payments[(short_channel_id, htlc.htlc_id)]
|
||||||
|
return success
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _check_invoice(invoice, amount_sat=None):
|
def _check_invoice(invoice, amount_sat=None):
|
||||||
|
|
|
@ -101,7 +101,7 @@ if [[ $1 == "redeem_htlcs" ]]; then
|
||||||
sleep 10
|
sleep 10
|
||||||
# alice pays bob
|
# alice pays bob
|
||||||
invoice=$($bob addinvoice 0.05 "test")
|
invoice=$($bob addinvoice 0.05 "test")
|
||||||
$alice lnpay $invoice
|
$alice lnpay $invoice || true
|
||||||
sleep 1
|
sleep 1
|
||||||
settled=$($alice list_channels | jq '.[] | .local_htlcs | .settles | length')
|
settled=$($alice list_channels | jq '.[] | .local_htlcs | .settles | length')
|
||||||
if [[ "$settled" != "0" ]]; then
|
if [[ "$settled" != "0" ]]; then
|
||||||
|
|
|
@ -90,6 +90,7 @@ class MockLNWallet:
|
||||||
self.inflight = {}
|
self.inflight = {}
|
||||||
self.wallet = MockWallet()
|
self.wallet = MockWallet()
|
||||||
self.localfeatures = LnLocalFeatures(0)
|
self.localfeatures = LnLocalFeatures(0)
|
||||||
|
self.pending_payments = defaultdict(asyncio.Future)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def lock(self):
|
def lock(self):
|
||||||
|
@ -216,25 +217,12 @@ class TestPeer(SequentialTestCase):
|
||||||
w2.invoices[bh2u(RHASH)] = (pay_req, True, False)
|
w2.invoices[bh2u(RHASH)] = (pay_req, True, False)
|
||||||
return pay_req
|
return pay_req
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def prepare_ln_message_future(w2 # receiver
|
|
||||||
):
|
|
||||||
fut = asyncio.Future()
|
|
||||||
def evt_set(event, _lnwallet, msg, _htlc_id):
|
|
||||||
fut.set_result(msg)
|
|
||||||
w2.network.register_callback(evt_set, ['ln_message'])
|
|
||||||
return fut
|
|
||||||
|
|
||||||
def test_payment(self):
|
def test_payment(self):
|
||||||
p1, p2, w1, w2, _q1, _q2 = self.prepare_peers()
|
p1, p2, w1, w2, _q1, _q2 = self.prepare_peers()
|
||||||
pay_req = self.prepare_invoice(w2)
|
pay_req = self.prepare_invoice(w2)
|
||||||
fut = self.prepare_ln_message_future(w2)
|
|
||||||
|
|
||||||
async def pay():
|
async def pay():
|
||||||
addr, peer, coro = await LNWallet._pay(w1, pay_req, same_thread=True)
|
result = await LNWallet._pay(w1, pay_req)
|
||||||
await coro
|
self.assertEqual(result, True)
|
||||||
print("HTLC ADDED")
|
|
||||||
self.assertEqual(await fut, 'Payment received')
|
|
||||||
gath.cancel()
|
gath.cancel()
|
||||||
gath = asyncio.gather(pay(), p1._message_loop(), p2._message_loop())
|
gath = asyncio.gather(pay(), p1._message_loop(), p2._message_loop())
|
||||||
async def f():
|
async def f():
|
||||||
|
|
Loading…
Add table
Reference in a new issue