Skip to content

Commit

Permalink
fix: always watch temporarily failed xpay payments
Browse files Browse the repository at this point in the history
  • Loading branch information
michael1011 committed Feb 2, 2025
1 parent 9c92159 commit 3c38b61
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 34 deletions.
6 changes: 1 addition & 5 deletions lib/lightning/PendingPaymentTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,7 @@ class PendingPaymentTracker {
this.lightningTrackers[lightningClient.type].isPermanentError(e);

// CLN xpay does throw errors while the payment is still pending
if (
lightningClient.type === NodeType.CLN &&
!isPermanentError &&
ClnPendingPaymentTracker.shouldBeWatched(e)
) {
if (lightningClient.type === NodeType.CLN && !isPermanentError) {
this.lightningTrackers[lightningClient.type].watchPayment(
lightningClient,
swap.invoice!,
Expand Down
37 changes: 26 additions & 11 deletions lib/lightning/cln/ClnClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -658,18 +658,14 @@ class ClnClient
public checkPayStatus = async (
invoice: string,
): Promise<PaymentResponse | undefined> => {
const decoded = await this.decodeInvoice(invoice);

const listPayReq = new noderpc.ListpaysRequest();
listPayReq.setBolt11(invoice);

const pays = (
await this.unaryNodeCall<
noderpc.ListpaysRequest,
noderpc.ListpaysResponse
>('listPays', listPayReq, false)
).getPaysList();
const { decoded, pays } = await this.listPays(invoice);
return this.checkListPaysStatus(decoded, pays);
};

public checkListPaysStatus = async (
decoded: Awaited<ReturnType<typeof this.decodeInvoice>>,
pays: noderpc.ListpaysPays[],
): Promise<PaymentResponse | undefined> => {
// Check if the payment succeeded, ...
const completedAttempts = pays.filter(
(attempt) => attempt.getStatus() === ListpaysPaysStatus.COMPLETE,
Expand Down Expand Up @@ -728,6 +724,25 @@ class ClnClient
return undefined;
};

public listPays = async (invoice: string) => {
const decoded = await this.decodeInvoice(invoice);

const listPayReq = new noderpc.ListpaysRequest();
listPayReq.setBolt11(invoice);

const pays = (
await this.unaryNodeCall<
noderpc.ListpaysRequest,
noderpc.ListpaysResponse
>('listPays', listPayReq, false)
).getPaysList();

return {
pays,
decoded,
};
};

public subscribeTrackHoldInvoices = () => {
if (this.trackAllSubscription) {
this.trackAllSubscription.cancel();
Expand Down
33 changes: 15 additions & 18 deletions lib/lightning/paymentTrackers/ClnPendingPaymentTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,6 @@ class ClnPendingPaymentTracker extends NodePendingPendingTracker {
);
}

public static shouldBeWatched = (error: unknown) => {
const msg = formatError(error);
return (
(msg.includes('Failed after') && msg.includes('attempts')) ||
msg.includes('xpay') ||
msg === 'Connection dropped'
);
};

public stop = () => {
clearInterval(this.checkInterval as unknown as number);
};
Expand All @@ -52,10 +43,7 @@ class ClnPendingPaymentTracker extends NodePendingPendingTracker {
.then((result) => this.handleSucceededPayment(preimageHash, result))
.catch((error) => {
// CLN xpay throws errors while the payment is still pending
if (
!this.isPermanentError(error) &&
ClnPendingPaymentTracker.shouldBeWatched(error)
) {
if (!this.isPermanentError(error)) {
this.watchPayment(client, invoice, preimageHash);
} else {
this.handleFailedPayment(client, preimageHash, error);
Expand Down Expand Up @@ -93,12 +81,21 @@ class ClnPendingPaymentTracker extends NodePendingPendingTracker {
{ client, invoice },
] of this.paymentsToWatch.entries()) {
try {
const res = await client.checkPayStatus(invoice);
if (res === undefined) {
continue;
}
const { decoded, pays } = await client.listPays(invoice);
const res = await client.checkListPaysStatus(decoded, pays);
if (pays.length === 0) {
this.handleFailedPayment(
client,
preimageHash,
'no attempts have been made',
);
} else {
if (res === undefined) {
continue;
}

await this.handleSucceededPayment(preimageHash, res);
await this.handleSucceededPayment(preimageHash, res);
}
} catch (e) {
// Ignore when the payment is pending; it's not a payment error
if (e === ClnClient.paymentPendingError) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,17 @@ describe('ClnPendingPaymentTracker', () => {
tracker.trackPayment(clnClient, preimageHash, invoice, promise);
await expect(promise).rejects.toEqual(expect.anything());

await tracker['checkPendingPayments']();

expect(LightningPaymentRepository.setStatus).toHaveBeenCalledTimes(1);
expect(LightningPaymentRepository.setStatus).toHaveBeenCalledWith(
preimageHash,
NodeType.CLN,
LightningPaymentStatus.TemporaryFailure,
undefined,
);

expect(tracker['paymentsToWatch'].size).toEqual(0);
});
});

Expand Down

0 comments on commit 3c38b61

Please sign in to comment.