Skip to content

Commit

Permalink
fix: throw error when all CLN payment attempts failed
Browse files Browse the repository at this point in the history
  • Loading branch information
michael1011 committed Jan 24, 2025
1 parent 7dfeece commit 2735e21
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 5 deletions.
27 changes: 22 additions & 5 deletions lib/lightning/cln/ClnClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class ClnClient
public static readonly moddedVersionSuffix = '-';

public static readonly paymentPendingError = 'payment already pending';
public static readonly paymentAllAttemptsFailed = 'all attempts failed';

private static readonly paymentMinFee = 121;
private static readonly paymentTimeout = 300;
Expand Down Expand Up @@ -535,9 +536,16 @@ class ClnClient
_?: string,
maxPaymentFeeRatio?: number,
): Promise<PaymentResponse> => {
const payStatus = await this.checkPayStatus(invoice);
if (payStatus !== undefined) {
return payStatus;
try {
const payStatus = await this.checkPayStatus(invoice);
if (payStatus !== undefined) {
return payStatus;
}
} catch (e) {
// When all attempts failed, we are ready for another try
if (e !== ClnClient.paymentAllAttemptsFailed) {
throw e;
}
}

const decoded = await this.decodeInvoice(invoice);
Expand Down Expand Up @@ -662,7 +670,7 @@ class ClnClient
>('listPays', listPayReq, false)
).getPaysList();

// Check if the payment succeeded...
// Check if the payment succeeded, ...
const completedAttempts = pays.filter(
(attempt) => attempt.getStatus() === ListpaysPaysStatus.COMPLETE,
);
Expand All @@ -680,7 +688,7 @@ class ClnClient
};
}

// ... or is still pending
// ... is still pending ...
const hasPendingPayments = pays.some(
(pay) => pay.getStatus() === ListpaysPaysStatus.PENDING,
);
Expand Down Expand Up @@ -708,6 +716,15 @@ class ClnClient
}
}

// ... or has failed
// TODO: update when xpay persists errors from previous attempts
if (
pays.length > 0 &&
pays.every((pay) => pay.getStatus() === ListpaysPaysStatus.FAILED)
) {
throw ClnClient.paymentAllAttemptsFailed;
}

return undefined;
};

Expand Down
25 changes: 25 additions & 0 deletions test/integration/lightning/cln/ClnClient.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { randomBytes } from 'crypto';
import { getHexString, getUnixTime } from '../../../../lib/Utils';
import Errors from '../../../../lib/lightning/Errors';
import { InvoiceFeature } from '../../../../lib/lightning/LightningClient';
import ClnClient from '../../../../lib/lightning/cln/ClnClient';
import * as noderpc from '../../../../lib/proto/cln/node_pb';
import * as primitivesrpc from '../../../../lib/proto/cln/primitives_pb';
import Sidecar from '../../../../lib/sidecar/Sidecar';
Expand Down Expand Up @@ -298,6 +299,30 @@ describe('ClnClient', () => {
});
});

describe('checkPayStatus', () => {
test('should throw when all attempts failed', async () => {
const invoice = await bitcoinLndClient.addInvoice(10_000);
await bitcoinLndClient.cancelHoldInvoice(
Buffer.from(invoice.rHash as string, 'base64'),
);

await expect(
clnClient.sendPayment(invoice.paymentRequest),
).rejects.toEqual(expect.anything());

await expect(
clnClient.checkPayStatus(invoice.paymentRequest),
).rejects.toEqual(ClnClient.paymentAllAttemptsFailed);
});

test('should not throw when no attempts have been made', async () => {
const invoice = await bitcoinLndClient.addInvoice(10_000);
await expect(
clnClient.checkPayStatus(invoice.paymentRequest),
).resolves.toBeUndefined();
});
});

test('should subscribe to single invoices', async () => {
expect.assertions(4);

Expand Down

0 comments on commit 2735e21

Please sign in to comment.