Skip to content

Commit

Permalink
feat(lightningd): add Core Lightning GRPC info to sidebar
Browse files Browse the repository at this point in the history
  • Loading branch information
jamaljsr committed May 30, 2022
1 parent cfce43c commit cd1021e
Show file tree
Hide file tree
Showing 10 changed files with 84 additions and 12 deletions.
4 changes: 4 additions & 0 deletions src/components/designer/lightning/ConnectTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export interface ConnectionInfo {
readOnly?: string;
invoice?: string;
cert?: string;
certKey?: string;
basicAuth?: string;
};
p2pUriExternal: string;
Expand Down Expand Up @@ -84,8 +85,11 @@ const ConnectTab: React.FC<Props> = ({ node }) => {
return {
restUrl: `http://127.0.0.1:${cln.ports.rest}`,
restDocsUrl: 'https://github.com/Ride-The-Lightning/c-lightning-REST',
grpcUrl: cln.ports.grpc ? `127.0.0.1:${cln.ports.grpc}` : undefined,
credentials: {
admin: cln.paths.macaroon,
cert: cln.paths.tlsCert,
certKey: cln.paths.tlsKey,
},
p2pUriExternal: `${pubkey}@127.0.0.1:${cln.ports.p2p}`,
authTypes: ['paths', 'hex', 'base64'],
Expand Down
27 changes: 25 additions & 2 deletions src/components/designer/lightning/connect/EncodedStrings.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { waitFor } from '@testing-library/react';
import { LndNode } from 'shared/types';
import { CLightningNode, LndNode } from 'shared/types';
import * as files from 'utils/files';
import { getNetwork, renderWithProviders } from 'utils/tests';
import { ConnectionInfo } from '../ConnectTab';
Expand All @@ -25,7 +25,7 @@ describe('EncodedStrings', () => {
filesMock.read.mockResolvedValue('file-content');
});

it('should display credentials', async () => {
it('should display credentials for LND', async () => {
const lnd = network.nodes.lightning[0] as LndNode;
const lndCreds: ConnectionInfo['credentials'] = {
admin: lnd.paths.adminMacaroon,
Expand All @@ -48,6 +48,29 @@ describe('EncodedStrings', () => {
);
});

it('should display credentials for Core Lightning', async () => {
const cln = network.nodes.lightning[1] as CLightningNode;
const clnCreds: ConnectionInfo['credentials'] = {
admin: cln.paths.macaroon,
cert: cln.paths.tlsCert,
certKey: cln.paths.tlsKey,
};
const { getByText } = renderComponent(clnCreds, 'hex');
await waitFor(() => getByText('TLS Cert'));
expect(getByText('TLS Cert')).toBeInTheDocument();
expect(getByText('TLS Key')).toBeInTheDocument();
expect(getByText('Admin Macaroon')).toBeInTheDocument();
expect(filesMock.read).toBeCalledWith(expect.stringContaining('client.pem'), 'hex');
expect(filesMock.read).toBeCalledWith(
expect.stringContaining('client-key.pem'),
'hex',
);
expect(filesMock.read).toBeCalledWith(
expect.stringContaining('access.macaroon'),
'hex',
);
});

it('should handle all missing credentials', async () => {
const missingCreds = {} as ConnectionInfo['credentials'];
const { queryByText } = renderComponent(missingCreds, 'hex');
Expand Down
3 changes: 2 additions & 1 deletion src/components/designer/lightning/connect/EncodedStrings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ const EncodedStrings: React.FC<Props> = ({ encoding, credentials }) => {
const { notify } = useStoreActions(s => s.app);
const [encodedValues, setEncodedValues] = useState<Record<string, string>>({});
useAsync(async () => {
const { cert, admin, invoice, readOnly } = credentials;
const { cert, certKey, admin, invoice, readOnly } = credentials;
try {
const values: Record<string, string> = {};
if (cert) values[l('tlsCert')] = await read(cert, encoding);
if (certKey) values[l('tlsKey')] = await read(certKey, encoding);
if (admin) values[l('adminMacaroon')] = await read(admin, encoding);
if (invoice) values[l('invoiceMacaroon')] = await read(invoice, encoding);
if (readOnly) values[l('readOnlyMacaroon')] = await read(readOnly, encoding);
Expand Down
3 changes: 2 additions & 1 deletion src/components/designer/lightning/connect/FilePaths.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ interface Props {

const FilePaths: React.FC<Props> = ({ credentials }) => {
const { l } = usePrefixedTranslation('cmps.designer.lightning.connect.FilePaths');
const { cert, admin, invoice, readOnly } = credentials;
const { cert, certKey, admin, invoice, readOnly } = credentials;

const auth: DetailValues = [
[l('tlsCert'), cert, cert && ellipseInner(cert, 14, 22)],
[l('tlsKey'), certKey, certKey && ellipseInner(certKey, 14, 22)],
[l('adminMacaroon'), admin, admin && ellipseInner(admin, 14, 22)],
[l('invoiceMacaroon'), invoice, invoice && ellipseInner(invoice, 14, 22)],
[l('readOnlyMacaroon'), readOnly, readOnly && ellipseInner(readOnly, 14, 22)],
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -179,10 +179,12 @@
"cmps.designer.lightning.actions.PaymentButtons.payInvoice": "Pay Invoice",
"cmps.designer.lightning.actions.PaymentButtons.createInvoice": "Create Invoice",
"cmps.designer.lightning.connect.FilePaths.tlsCert": "TLS Cert",
"cmps.designer.lightning.connect.FilePaths.tlsKey": "TLS Key",
"cmps.designer.lightning.connect.FilePaths.adminMacaroon": "Admin Macaroon",
"cmps.designer.lightning.connect.FilePaths.invoiceMacaroon": "Invoice Macaroon",
"cmps.designer.lightning.connect.FilePaths.readOnlyMacaroon": "Read-only Macaroon",
"cmps.designer.lightning.connect.EncodedStrings.tlsCert": "TLS Cert",
"cmps.designer.lightning.connect.EncodedStrings.tlsKey": "TLS Key",
"cmps.designer.lightning.connect.EncodedStrings.adminMacaroon": "Admin Macaroon",
"cmps.designer.lightning.connect.EncodedStrings.invoiceMacaroon": "Invoice Macaroon",
"cmps.designer.lightning.connect.EncodedStrings.readOnlyMacaroon": "Read-only Macaroon",
Expand Down
16 changes: 13 additions & 3 deletions src/lib/docker/composeFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ class ComposeFile {

addClightning(node: CLightningNode, backend: CommonNode) {
const { name, version, ports } = node;
const { rest, p2p } = ports;
const { rest, p2p, grpc } = ports;
const container = getContainerName(node);
// define the variable substitutions
const variables = {
Expand All @@ -103,11 +103,21 @@ class ComposeFile {
const image =
node.docker.image || `${dockerConfigs['c-lightning'].imageName}:${version}`;
// use the node's custom command or the default for the implementation
const nodeCommand = node.docker.command || dockerConfigs['c-lightning'].command;
let nodeCommand = node.docker.command || dockerConfigs['c-lightning'].command;
// do not include the GRPC port arg in the command for unsupported versions
if (grpc === 0) nodeCommand = nodeCommand.replace('--grpc-port=11001', '');
// replace the variables in the command
const command = this.mergeCommand(nodeCommand, variables);
// add the docker service
this.content.services[name] = clightning(name, container, image, rest, p2p, command);
this.content.services[name] = clightning(
name,
container,
image,
rest,
grpc,
p2p,
command,
);
}

addEclair(node: EclairNode, backend: CommonNode) {
Expand Down
7 changes: 5 additions & 2 deletions src/lib/docker/nodeTemplates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export const clightning = (
container: string,
image: string,
restPort: number,
grpcPort: number,
p2pPort: number,
command: string,
): ComposeService => ({
Expand All @@ -94,12 +95,14 @@ export const clightning = (
],
expose: [
'8080', // REST
grpcPort ? '11001' : '', // GRPC
'9735', // p2p
],
].filter(p => !!p), // filter out empty strings
ports: [
`${restPort}:8080`, // REST
grpcPort ? `${grpcPort}:11001` : '', // REST
`${p2pPort}:9735`, // p2p
],
].filter(p => !!p), // filer out empty strings
});

export const eclair = (
Expand Down
3 changes: 3 additions & 0 deletions src/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,12 @@ export interface LndNode extends LightningNode {
export interface CLightningNode extends LightningNode {
paths: {
macaroon: string;
tlsCert?: string;
tlsKey?: string;
};
ports: {
rest: number;
grpc: number;
p2p: number;
};
}
Expand Down
2 changes: 2 additions & 0 deletions src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const BasePorts: Record<NodeImplementation, Record<string, number>> = {
'c-lightning': {
rest: 8181,
p2p: 9835,
grpc: 11001,
},
eclair: {
rest: 8281,
Expand Down Expand Up @@ -125,6 +126,7 @@ export const dockerConfigs: Record<NodeImplementation, DockerConfig> = {
'--log-level=debug',
'--dev-bitcoind-poll=2',
'--dev-fast-gossip',
'--grpc-port=11001',
'--plugin=/opt/c-lightning-rest/plugin.js',
'--rest-port=8080',
'--rest-protocol=http',
Expand Down
29 changes: 26 additions & 3 deletions src/utils/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,16 @@ export const getLndFilePaths = (name: string, network: Network) => {
};
};

export const getCLightningFilePaths = (name: string, network: Network) => {
export const getCLightningFilePaths = (
name: string,
withTls: boolean,
network: Network,
) => {
const path = nodePath(network, 'c-lightning', name);
return {
macaroon: join(path, 'rest-api', 'access.macaroon'),
tlsCert: withTls ? join(path, 'lightningd', 'regtest', 'client.pem') : undefined,
tlsKey: withTls ? join(path, 'lightningd', 'regtest', 'client-key.pem') : undefined,
};
};

Expand Down Expand Up @@ -164,6 +170,8 @@ export const createCLightningNetworkNode = (
compatibility,
bitcoin,
);
// determines if GRPC is supported in a version of Core Lightning provided
const supportsGrpc = !isVersionCompatible(version, '0.10.2');
const id = lightning.length ? Math.max(...lightning.map(n => n.id)) + 1 : 0;
const name = getName(id);
return {
Expand All @@ -176,9 +184,10 @@ export const createCLightningNetworkNode = (
status,
// alternate between backend nodes
backendName: backends[id % backends.length].name,
paths: getCLightningFilePaths(name, network),
paths: getCLightningFilePaths(name, supportsGrpc, network),
ports: {
rest: BasePorts['c-lightning'].rest + id,
grpc: supportsGrpc ? BasePorts['c-lightning'].grpc + id : 0,
p2p: BasePorts['c-lightning'].p2p + id,
},
docker,
Expand Down Expand Up @@ -407,6 +416,11 @@ export const getOpenPortRange = async (requestedPorts: number[]): Promise<number
const openPorts: number[] = [];

for (let port of requestedPorts) {
// keep 0 port as this indicates the port isn't type isn't supported for the node
if (port === 0) {
openPorts.push(0);
continue;
}
if (openPorts.length) {
// adjust to check after the previous open port if necessary, since the last
// open port may have increased
Expand Down Expand Up @@ -530,6 +544,14 @@ export const getOpenPorts = async (network: Network): Promise<OpenPorts | undefi
});
}

existingPorts = clightning.map(n => n.ports.grpc);
openPorts = await getOpenPortRange(existingPorts);
if (openPorts.join() !== existingPorts.join()) {
openPorts.forEach((port, index) => {
ports[clightning[index].name] = { grpc: port };
});
}

existingPorts = clightning.map(n => n.ports.p2p);
openPorts = await getOpenPortRange(existingPorts);
if (openPorts.join() !== existingPorts.join()) {
Expand Down Expand Up @@ -638,7 +660,8 @@ export const importNetworkFromZip = async (
lnd.paths = getLndFilePaths(lnd.name, network);
} else if (ln.implementation === 'c-lightning') {
const cln = ln as CLightningNode;
cln.paths = getCLightningFilePaths(cln.name, network);
const supportsGrpc = cln.ports.grpc !== 0;
cln.paths = getCLightningFilePaths(cln.name, supportsGrpc, network);
} else if (ln.implementation !== 'eclair') {
throw new Error(l('unknownImplementation', { implementation: ln.implementation }));
}
Expand Down

0 comments on commit cd1021e

Please sign in to comment.