Skip to content

Commit

Permalink
refactor: use new hold plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
michael1011 committed Aug 23, 2024
1 parent a5a3f4c commit b283e37
Show file tree
Hide file tree
Showing 69 changed files with 2,402 additions and 9,705 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ jobs:
steps:
- name: Check out code
uses: actions/checkout@v4
with:
submodules: 'true'

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
Expand Down Expand Up @@ -68,7 +70,7 @@ jobs:
run: npm run python:lint

- name: Start hold invoice plugin
run: docker exec regtest lightning-cli plugin start /root/hold.sh
run: npm run docker:cln:hold

- name: Start mpay plugin
run: docker exec regtest lightning-cli plugin start /root/mpay.sh
Expand All @@ -80,7 +82,7 @@ jobs:
run: node run-int.js

- name: Stop hold invoice plugin
run: docker exec regtest lightning-cli plugin stop /root/hold.sh
run: docker exec regtest lightning-cli plugin stop /root/.lightning/plugins/hold

- name: Stop mpay plugin
run: docker exec regtest lightning-cli plugin stop /root/mpay.sh
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,4 @@ docker/regtest/data/core/cookies/
docker/regtest/data/cln/hold
docker/regtest/data/cln/certs
docker/regtest/data/cln/mpay
docker/regtest/data/cln/plugins
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "hold"]
path = hold
url = https://github.com/boltzExchange/hold
1 change: 1 addition & 0 deletions docker/regtest/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ RUN apt-get update && \
python3 \
libtool \
autoconf \
libpq-dev \
libdb-dev \
pkg-config \
python3-pip \
Expand Down
2 changes: 2 additions & 0 deletions docker/regtest/startRegtest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ docker run \
--volume "${PWD}"/docker/regtest/data/cln/hold:/root/.lightning/regtest/hold \
--volume "${PWD}"/docker/regtest/data/cln/mpay:/root/.lightning/regtest/mpay \
--volume "${PWD}"/tools:/tools \
--volume "${PWD}"/docker/regtest/data/cln/plugins:/root/.lightning/plugins \
-p 29000:29000 \
-p 29001:29001 \
-p 29002:29002 \
Expand All @@ -32,3 +33,4 @@ docker run \

docker exec regtest bash -c "cp /root/.lightning/regtest/*.pem /root/.lightning/regtest/certs"
docker exec regtest chmod -R 777 /root/.lightning/regtest/certs
docker exec regtest chmod -R 777 /root/.lightning/plugins
1 change: 1 addition & 0 deletions hold
Submodule hold added at 50c69b
4 changes: 2 additions & 2 deletions lib/VersionCheck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ class VersionCheck {
maximal: '24.05',
},
[ClnClient.serviceNameHold]: {
minimal: '0.0.3',
maximal: '0.0.5',
minimal: '0.1.0',
maximal: '0.1.0',
},
[MpayClient.serviceName]: {
minimal: '0.1.0',
Expand Down
156 changes: 49 additions & 107 deletions lib/lightning/cln/ClnClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,7 @@ import * as primitivesrpc from '../../proto/cln/primitives_pb';
import { HoldClient } from '../../proto/hold/hold_grpc_pb';
import * as holdrpc from '../../proto/hold/hold_pb';
import { WalletBalance } from '../../wallet/providers/WalletProviderInterface';
import {
msatToSat,
satToMsat,
scidClnToLnd,
scidLndToCln,
} from '../ChannelUtils';
import { msatToSat, satToMsat, scidClnToLnd } from '../ChannelUtils';
import Errors from '../Errors';
import { grpcOptions, unaryCall } from '../GrpcUtils';
import {
Expand All @@ -47,6 +42,7 @@ import {
calculatePaymentFee,
} from '../LightningClient';
import Mpay from './MpayClient';
import { getRoute } from './Router';
import { ClnConfig, createSsl } from './Types';

import ListpaysPaysStatus = ListpaysPays.ListpaysPaysStatus;
Expand Down Expand Up @@ -354,14 +350,26 @@ class ClnClient
};

public routingHints = async (node: string): Promise<HopHint[][]> => {
const req = new holdrpc.RoutingHintsRequest();
req.setNode(node);
const req = new noderpc.ListpeerchannelsRequest();
req.setId(node);

const res = await this.unaryHoldCall<
holdrpc.RoutingHintsRequest,
holdrpc.RoutingHintsResponse.AsObject
>('routingHints', req);
return ClnClient.routingHintsFromGrpc(res.hintsList);
const channels = await this.unaryNodeCall<
noderpc.ListpeerchannelsRequest,
noderpc.ListpeerchannelsResponse.AsObject
>('listPeerChannels', req, true);

return channels.channelsList
.filter((chan) => chan.pb_private)
.map((channel) => [
{
nodeId: node,
chanId: scidClnToLnd(getHexString(Buffer.from(channel.channelId))),
feeBaseMsat: channel.updates!.remote!.feeBaseMsat!.msat,
feeProportionalMillionths:
channel.updates!.remote!.feeProportionalMillionths,
cltvExpiryDelta: channel.updates!.remote!.cltvExpiryDelta,
},
]);
};

public addHoldInvoice = async (
Expand All @@ -375,7 +383,7 @@ class ClnClient
): Promise<string> => {
const req = new holdrpc.InvoiceRequest();
req.setAmountMsat(satToMsat(value));
req.setPaymentHash(getHexString(preimageHash));
req.setPaymentHash(preimageHash);

if (cltvExpiry) {
req.setMinFinalCltvExpiry(cltvExpiry);
Expand All @@ -386,11 +394,11 @@ class ClnClient
}

if (memo) {
req.setDescription(memo);
req.setMemo(memo);
}

if (descriptionHash) {
req.setDescriptionHash(getHexString(descriptionHash));
req.setHash(descriptionHash);
}

if (routingHints) {
Expand All @@ -407,7 +415,7 @@ class ClnClient

public lookupHoldInvoice = async (preimageHash: Buffer): Promise<Invoice> => {
const req = new holdrpc.ListRequest();
req.setPaymentHash(getHexString(preimageHash));
req.setPaymentHash(preimageHash);

const res = await this.unaryHoldCall<
holdrpc.ListRequest,
Expand All @@ -427,7 +435,7 @@ class ClnClient

public cancelHoldInvoice = async (preimageHash: Buffer): Promise<void> => {
const req = new holdrpc.CancelRequest();
req.setPaymentHash(getHexString(preimageHash));
req.setPaymentHash(preimageHash);

await this.unaryHoldCall<
holdrpc.CancelRequest,
Expand All @@ -437,7 +445,7 @@ class ClnClient

public settleHoldInvoice = async (preimage: Buffer): Promise<void> => {
const req = new holdrpc.SettleRequest();
req.setPaymentPreimage(getHexString(preimage));
req.setPaymentPreimage(preimage);

await this.unaryHoldCall<
holdrpc.SettleRequest,
Expand Down Expand Up @@ -510,7 +518,7 @@ class ClnClient
routingHints?: HopHint[][],
): Promise<Route[]> => {
const prms: Promise<Route>[] = [
this.queryRoute(destination, amt, cltvLimit, finalCltvDelta),
getRoute(this.unaryNodeCall, destination, amt, cltvLimit, finalCltvDelta),
];

if (routingHints) {
Expand All @@ -520,7 +528,8 @@ class ClnClient
0,
);
prms.push(
this.queryRoute(
getRoute(
this.unaryNodeCall,
hint[0].nodeId,
amt,
cltvLimit,
Expand Down Expand Up @@ -614,7 +623,7 @@ class ClnClient
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public subscribeSingleInvoice = (_: Buffer): void => {
// Just here for interface compatibility;
// with CLN we can subscribe to all hold invoice with one gRPC subscription
// with CLN we can subscribe to all hold invoices with one gRPC subscription
return;
};

Expand All @@ -625,8 +634,8 @@ class ClnClient
const routeHint = new holdrpc.RoutingHint();
for (const hint of hints) {
const hopHint = new holdrpc.Hop();
hopHint.setPublicKey(hint.nodeId);
hopHint.setShortChannelId(scidLndToCln(hint.chanId));
hopHint.setPublicKey(getHexBuffer(hint.nodeId));
hopHint.setShortChannelId(Number(hint.chanId));
hopHint.setBaseFee(hint.feeBaseMsat);
hopHint.setPpmFee(hint.feeProportionalMillionths);
hopHint.setCltvExpiryDelta(hint.cltvExpiryDelta);
Expand All @@ -638,49 +647,39 @@ class ClnClient
});
};

private static routingHintsFromGrpc = (
routingHints: holdrpc.RoutingHint.AsObject[],
): HopHint[][] => {
return routingHints.map((hint) =>
hint.hopsList.map((hop) => ({
nodeId: hop.publicKey,
chanId: hop.shortChannelId,
feeBaseMsat: hop.baseFee,
feeProportionalMillionths: hop.ppmFee,
cltvExpiryDelta: hop.cltvExpiryDelta,
})),
);
};

private static invoiceStateFromGrpc = (
state: holdrpc.InvoiceState,
): InvoiceState => {
switch (state) {
case holdrpc.InvoiceState.INVOICE_UNPAID:
case holdrpc.InvoiceState.UNPAID:
return InvoiceState.Open;
case holdrpc.InvoiceState.INVOICE_ACCEPTED:
case holdrpc.InvoiceState.ACCEPTED:
return InvoiceState.Accepted;
case holdrpc.InvoiceState.INVOICE_CANCELLED:
case holdrpc.InvoiceState.CANCELLED:
return InvoiceState.Cancelled;
case holdrpc.InvoiceState.INVOICE_PAID:
case holdrpc.InvoiceState.PAID:
return InvoiceState.Settled;
}
};

private static htlcFromGrpc = (htlc: holdrpc.Htlc.AsObject): Htlc => {
return {
state: ClnClient.htlcStateFromGrpc(htlc.state),
valueMsat: htlc.msat,
state: ClnClient.htlcStateFromGrpc(htlc.state),
};
};

private static htlcStateFromGrpc = (state: holdrpc.HtlcState): HtlcState => {
private static htlcStateFromGrpc = (
state: holdrpc.InvoiceState,
): HtlcState => {
switch (state) {
case holdrpc.HtlcState.HTLC_ACCEPTED:
case holdrpc.InvoiceState.UNPAID:
throw 'invalid HTLC state';
case holdrpc.InvoiceState.ACCEPTED:
return HtlcState.Accepted;
case holdrpc.HtlcState.HTLC_CANCELLED:
case holdrpc.InvoiceState.CANCELLED:
return HtlcState.Cancelled;
case holdrpc.HtlcState.HTLC_SETTLED:
case holdrpc.InvoiceState.PAID:
return HtlcState.Settled;
}
};
Expand Down Expand Up @@ -721,24 +720,7 @@ class ClnClient
};
}

// ... has failed
// TODO: mpay incorrect payment details detection
for (const payStatus of (await this.payStatus(invoice)).statusList) {
for (const attempt of payStatus.attemptsList) {
if (
attempt.state ===
holdrpc.PayStatusResponse.PayStatus.Attempt.AttemptState
.ATTEMPT_PENDING ||
attempt.failure === undefined
) {
continue;
}

if (ClnClient.errIsIncorrectPaymentDetails(attempt.failure.message)) {
throw attempt.failure.message;
}
}
}
// TODO: find a way to check "paystatus"

// ... or is still pending
const hasPendingPayments = pays.some(
Expand Down Expand Up @@ -771,46 +753,6 @@ class ClnClient
return undefined;
};

private payStatus = (invoice: string) => {
const req = new holdrpc.PayStatusRequest();
req.setBolt11(invoice);

return this.unaryHoldCall<
holdrpc.PayStatusRequest,
holdrpc.PayStatusResponse.AsObject
>('payStatus', req);
};

private queryRoute = async (
destination: string,
amt: number,
cltvLimit?: number,
finalCltvDelta?: number,
): Promise<Route> => {
const req = new holdrpc.GetRouteRequest();
req.setDestination(destination);
req.setRiskFactor(0);
req.setAmountMsat(amt);

if (cltvLimit) {
req.setMaxCltv(cltvLimit);
}

if (finalCltvDelta) {
req.setFinalCltvDelta(finalCltvDelta);
}

const res = await this.unaryHoldCall<
holdrpc.GetRouteRequest,
holdrpc.GetRouteResponse.AsObject
>('getRoute', req);

return {
ctlv: res.hopsList[0].delay,
feesMsat: res.feesMsat,
};
};

private subscribeTrackHoldInvoices = () => {
if (this.trackAllSubscription) {
this.trackAllSubscription.cancel();
Expand All @@ -822,7 +764,7 @@ class ClnClient

this.trackAllSubscription.on('data', (update: holdrpc.TrackAllResponse) => {
switch (update.getState()) {
case holdrpc.InvoiceState.INVOICE_ACCEPTED:
case holdrpc.InvoiceState.ACCEPTED:
this.logger.debug(
`${ClnClient.serviceName} ${
this.symbol
Expand All @@ -832,7 +774,7 @@ class ClnClient
this.emit('htlc.accepted', update.getBolt11());
break;

case holdrpc.InvoiceState.INVOICE_PAID:
case holdrpc.InvoiceState.PAID:
this.logger.debug(
`${ClnClient.serviceName} ${
this.symbol
Expand Down
Loading

0 comments on commit b283e37

Please sign in to comment.