mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-09-05 05:15:12 +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')
|
||||
def lnpay(self, invoice):
|
||||
addr, peer, f = self.lnworker.pay(invoice)
|
||||
return f.result()
|
||||
return self.lnworker.pay(invoice, timeout=10)
|
||||
|
||||
@command('wn')
|
||||
def addinvoice(self, requested_amount, message):
|
||||
|
|
|
@ -1675,33 +1675,25 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
|
|||
|
||||
def pay_lightning_invoice(self, invoice):
|
||||
amount = self.amount_e.get_amount()
|
||||
LN_NUM_PAYMENT_ATTEMPTS = 1 # TODO increase
|
||||
|
||||
LN_NUM_PAYMENT_ATTEMPTS = 3
|
||||
def on_success(result):
|
||||
self.logger.info(f'ln payment success. {result}')
|
||||
self.do_clear()
|
||||
def on_failure(exc_info):
|
||||
type_, e, traceback = exc_info
|
||||
if isinstance(e, PaymentFailure):
|
||||
self.show_error(_('Payment failed. Tried {} times:\n{}')
|
||||
.format(LN_NUM_PAYMENT_ATTEMPTS, e))
|
||||
self.show_error(_('Payment failed. {}').format(e))
|
||||
elif isinstance(e, InvoiceError):
|
||||
self.show_error(_('InvoiceError: {}').format(e))
|
||||
else:
|
||||
raise e
|
||||
def task():
|
||||
failure_list = []
|
||||
for i in range(LN_NUM_PAYMENT_ATTEMPTS):
|
||||
try:
|
||||
addr, peer, future = self.wallet.lnworker.pay(invoice, amount_sat=amount)
|
||||
future.result()
|
||||
success = self.wallet.lnworker.pay(invoice, amount_sat=amount, timeout=30)
|
||||
if success:
|
||||
break
|
||||
except PaymentFailure as e:
|
||||
failure_list.append(e)
|
||||
# try again
|
||||
else:
|
||||
msg = '\n'.join(str(e) for e in failure_list)
|
||||
raise PaymentFailure(msg)
|
||||
raise PaymentFailure('Failed after {i} attempts')
|
||||
|
||||
msg = _('Sending lightning payment...')
|
||||
WaitingDialog(self, msg, task, on_success, on_failure)
|
||||
|
|
|
@ -973,7 +973,7 @@ class Peer(Logger):
|
|||
@log_exceptions
|
||||
async def _on_update_fail_htlc(self, chan, htlc_id, 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):
|
||||
chan = self.channels[channel_id]
|
||||
|
@ -1117,7 +1117,7 @@ class Peer(Logger):
|
|||
@log_exceptions
|
||||
async def _on_update_fulfill_htlc(self, chan, htlc_id, preimage, 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)
|
||||
|
||||
def on_update_fail_malformed_htlc(self, payload):
|
||||
|
|
|
@ -314,6 +314,7 @@ class LNWallet(LNWorker):
|
|||
c.set_local_commitment(c.current_commitment(LOCAL))
|
||||
# timestamps of opening and closing transactions
|
||||
self.channel_timestamps = self.storage.get('lightning_channel_timestamps', {})
|
||||
self.pending_payments = defaultdict(asyncio.Future)
|
||||
|
||||
def start_network(self, network: 'Network'):
|
||||
self.network = network
|
||||
|
@ -641,14 +642,15 @@ class LNWallet(LNWorker):
|
|||
chan = f.result(timeout)
|
||||
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
|
||||
one thread only.
|
||||
Can be called from other threads
|
||||
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(coro, self.network.asyncio_loop)
|
||||
return addr, peer, fut
|
||||
fut = asyncio.run_coroutine_threadsafe(
|
||||
self._pay(invoice, amount_sat),
|
||||
self.network.asyncio_loop)
|
||||
return fut.result(timeout=timeout)
|
||||
|
||||
def get_channel_by_short_id(self, short_channel_id):
|
||||
with self.lock:
|
||||
|
@ -656,15 +658,14 @@ class LNWallet(LNWorker):
|
|||
if chan.short_channel_id == short_channel_id:
|
||||
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)
|
||||
self.save_invoice(addr.paymenthash, invoice, SENT, is_paid=False)
|
||||
self.wallet.set_label(bh2u(addr.paymenthash), addr.get_description())
|
||||
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):
|
||||
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):
|
||||
short_channel_id = route[0].short_channel_id
|
||||
|
@ -674,6 +675,8 @@ class LNWallet(LNWorker):
|
|||
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())
|
||||
self.network.trigger_callback('htlc_added', htlc, addr, SENT)
|
||||
success = await self.pending_payments[(short_channel_id, htlc.htlc_id)]
|
||||
return success
|
||||
|
||||
@staticmethod
|
||||
def _check_invoice(invoice, amount_sat=None):
|
||||
|
|
|
@ -101,7 +101,7 @@ if [[ $1 == "redeem_htlcs" ]]; then
|
|||
sleep 10
|
||||
# alice pays bob
|
||||
invoice=$($bob addinvoice 0.05 "test")
|
||||
$alice lnpay $invoice
|
||||
$alice lnpay $invoice || true
|
||||
sleep 1
|
||||
settled=$($alice list_channels | jq '.[] | .local_htlcs | .settles | length')
|
||||
if [[ "$settled" != "0" ]]; then
|
||||
|
|
|
@ -90,6 +90,7 @@ class MockLNWallet:
|
|||
self.inflight = {}
|
||||
self.wallet = MockWallet()
|
||||
self.localfeatures = LnLocalFeatures(0)
|
||||
self.pending_payments = defaultdict(asyncio.Future)
|
||||
|
||||
@property
|
||||
def lock(self):
|
||||
|
@ -216,25 +217,12 @@ class TestPeer(SequentialTestCase):
|
|||
w2.invoices[bh2u(RHASH)] = (pay_req, True, False)
|
||||
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):
|
||||
p1, p2, w1, w2, _q1, _q2 = self.prepare_peers()
|
||||
pay_req = self.prepare_invoice(w2)
|
||||
fut = self.prepare_ln_message_future(w2)
|
||||
|
||||
async def pay():
|
||||
addr, peer, coro = await LNWallet._pay(w1, pay_req, same_thread=True)
|
||||
await coro
|
||||
print("HTLC ADDED")
|
||||
self.assertEqual(await fut, 'Payment received')
|
||||
result = await LNWallet._pay(w1, pay_req)
|
||||
self.assertEqual(result, True)
|
||||
gath.cancel()
|
||||
gath = asyncio.gather(pay(), p1._message_loop(), p2._message_loop())
|
||||
async def f():
|
||||
|
|
Loading…
Add table
Reference in a new issue