Browse Source

feat: make lightning nodes p2p port reachable on LAN

This commit changes lightning nodes to accept p2p connections
via LAN instead of only via the private network (172.x.x.x).
master
Hampus Sjöberg 5 years ago
committed by jamaljsr
parent
commit
20d77ebc70
  1. 15
      src/components/designer/lightning/ConnectTab.tsx
  2. 3
      src/i18n/locales/en-US.json
  3. 23
      src/lib/docker/composeFile.ts
  4. 1
      src/lib/docker/nodeTemplates.spec.ts
  5. 4
      src/lib/docker/nodeTemplates.ts
  6. 2
      src/shared/types.ts
  7. 2
      src/utils/constants.ts
  8. 19
      src/utils/network.spec.ts
  9. 29
      src/utils/network.ts

15
src/components/designer/lightning/ConnectTab.tsx

@ -40,6 +40,7 @@ export interface ConnectionInfo {
readOnly?: string; readOnly?: string;
cert?: string; cert?: string;
}; };
p2pUriExternal: string;
} }
interface Props { interface Props {
@ -51,8 +52,9 @@ const ConnectTab: React.FC<Props> = ({ node }) => {
const [authType, setAuthType] = useState<string>('paths'); const [authType, setAuthType] = useState<string>('paths');
const { openInBrowser } = useStoreActions(s => s.app); const { openInBrowser } = useStoreActions(s => s.app);
const nodeState = useStoreState(s => s.lightning.nodes[node.name]); const nodeState = useStoreState(s => s.lightning.nodes[node.name]);
const pubkey = nodeState && nodeState.info ? nodeState.info.pubkey : '';
const p2pLnUrlInternal = nodeState && nodeState.info ? nodeState.info.rpcUrl : '';
const lnUrl = nodeState && nodeState.info ? nodeState.info.rpcUrl : '';
const info = useMemo((): ConnectionInfo => { const info = useMemo((): ConnectionInfo => {
if (node.status === Status.Started) { if (node.status === Status.Started) {
if (node.implementation === 'LND') { if (node.implementation === 'LND') {
@ -67,6 +69,7 @@ const ConnectTab: React.FC<Props> = ({ node }) => {
readOnly: lnd.paths.readonlyMacaroon, readOnly: lnd.paths.readonlyMacaroon,
cert: lnd.paths.tlsCert, cert: lnd.paths.tlsCert,
}, },
p2pUriExternal: `${pubkey}@127.0.0.1:${lnd.ports.p2p}`,
}; };
} else if (node.implementation === 'c-lightning') { } else if (node.implementation === 'c-lightning') {
const cln = node as CLightningNode; const cln = node as CLightningNode;
@ -76,6 +79,7 @@ const ConnectTab: React.FC<Props> = ({ node }) => {
credentials: { credentials: {
admin: cln.paths.macaroon, admin: cln.paths.macaroon,
}, },
p2pUriExternal: `${pubkey}@127.0.0.1:${cln.ports.p2p}`,
}; };
} }
} }
@ -85,7 +89,7 @@ const ConnectTab: React.FC<Props> = ({ node }) => {
restDocsUrl: '', restDocsUrl: '',
credentials: {}, credentials: {},
} as ConnectionInfo; } as ConnectionInfo;
}, [node]); }, [node, pubkey]);
if (node.status !== Status.Started) { if (node.status !== Status.Started) {
return <>{l('notStarted')}</>; return <>{l('notStarted')}</>;
@ -95,7 +99,12 @@ const ConnectTab: React.FC<Props> = ({ node }) => {
const hosts: DetailValues = [ const hosts: DetailValues = [
[l('grpcHost'), grpcUrl, grpcUrl], [l('grpcHost'), grpcUrl, grpcUrl],
[l('restHost'), restUrl, restUrl], [l('restHost'), restUrl, restUrl],
[l('p2pLnUrl'), lnUrl, ellipseInner(lnUrl, 3, 19)], [l('p2pLnUrlInternal'), info.p2pUriExternal, ellipseInner(p2pLnUrlInternal, 3, 17)],
[
l('p2pLnUrlExternal'),
info.p2pUriExternal,
ellipseInner(info.p2pUriExternal, 3, 17),
],
] ]
.filter(h => !!h[1]) // exclude empty values .filter(h => !!h[1]) // exclude empty values
.map(([label, value, text]) => ({ .map(([label, value, text]) => ({

3
src/i18n/locales/en-US.json

@ -180,7 +180,8 @@
"cmps.designer.lightning.ConnectTab.notStarted": "Node needs to be started to view connection info", "cmps.designer.lightning.ConnectTab.notStarted": "Node needs to be started to view connection info",
"cmps.designer.lightning.ConnectTab.grpcHost": "GRPC Host", "cmps.designer.lightning.ConnectTab.grpcHost": "GRPC Host",
"cmps.designer.lightning.ConnectTab.restHost": "REST Host", "cmps.designer.lightning.ConnectTab.restHost": "REST Host",
"cmps.designer.lightning.ConnectTab.p2pLnUrl": "P2P LN Url", "cmps.designer.lightning.ConnectTab.p2pLnUrlInternal": "P2P Internal",
"cmps.designer.lightning.ConnectTab.p2pLnUrlExternal": "P2P External",
"cmps.designer.lightning.ConnectTab.apiDocs": "API Docs", "cmps.designer.lightning.ConnectTab.apiDocs": "API Docs",
"cmps.designer.lightning.ConnectTab.filePaths": "File Paths", "cmps.designer.lightning.ConnectTab.filePaths": "File Paths",
"cmps.designer.lightning.ConnectTab.hexStrings": "HEX", "cmps.designer.lightning.ConnectTab.hexStrings": "HEX",

23
src/lib/docker/composeFile.ts

@ -49,22 +49,37 @@ class ComposeFile {
const { const {
name, name,
version, version,
ports: { rest, grpc }, ports: { rest, grpc, p2p },
} = node; } = node;
const container = getContainerName(node); const container = getContainerName(node);
const backendName = getContainerName(backend); const backendName = getContainerName(backend);
this.content.services[name] = lnd(name, container, version, backendName, rest, grpc); this.content.services[name] = lnd(
name,
container,
version,
backendName,
rest,
grpc,
p2p,
);
} }
addClightning(node: CLightningNode, backend: CommonNode) { addClightning(node: CLightningNode, backend: CommonNode) {
const { const {
name, name,
version, version,
ports: { rest }, ports: { rest, p2p },
} = node; } = node;
const container = getContainerName(node); const container = getContainerName(node);
const backendName = getContainerName(backend); const backendName = getContainerName(backend);
this.content.services[name] = clightning(name, container, version, backendName, rest); this.content.services[name] = clightning(
name,
container,
version,
backendName,
rest,
p2p,
);
} }
} }

1
src/lib/docker/nodeTemplates.spec.ts

@ -17,6 +17,7 @@ describe('nodeTemplates', () => {
'btcnode1', 'btcnode1',
8080, 8080,
10009, 10009,
9735,
); );
expect(node.image).toContain('lnd'); expect(node.image).toContain('lnd');
expect(node.container_name).toEqual('polar-mynode'); expect(node.container_name).toEqual('polar-mynode');

4
src/lib/docker/nodeTemplates.ts

@ -62,6 +62,7 @@ export const lnd = (
backendName: string, backendName: string,
restPort: number, restPort: number,
grpcPort: number, grpcPort: number,
p2pPort: number,
): ComposeService => ({ ): ComposeService => ({
image: `polarlightning/lnd:${version}`, image: `polarlightning/lnd:${version}`,
container_name: container, container_name: container,
@ -99,6 +100,7 @@ export const lnd = (
ports: [ ports: [
`${restPort}:8080`, // REST `${restPort}:8080`, // REST
`${grpcPort}:10009`, // gRPC `${grpcPort}:10009`, // gRPC
`${p2pPort}:9735`, // p2p
], ],
}); });
@ -108,6 +110,7 @@ export const clightning = (
version: string, version: string,
backendName: string, backendName: string,
restPort: number, restPort: number,
p2pPort: number,
): ComposeService => ({ ): ComposeService => ({
image: `polarlightning/clightning:${version}`, image: `polarlightning/clightning:${version}`,
container_name: container, container_name: container,
@ -143,5 +146,6 @@ export const clightning = (
], ],
ports: [ ports: [
`${restPort}:8080`, // REST `${restPort}:8080`, // REST
`${p2pPort}:9735`, // p2p
], ],
}); });

2
src/shared/types.ts

@ -33,6 +33,7 @@ export interface LndNode extends LightningNode {
ports: { ports: {
rest: number; rest: number;
grpc: number; grpc: number;
p2p: number;
}; };
} }
@ -42,6 +43,7 @@ export interface CLightningNode extends LightningNode {
}; };
ports: { ports: {
rest: number; rest: number;
p2p: number;
}; };
} }

2
src/utils/constants.ts

@ -51,9 +51,11 @@ export const BasePorts = {
lnd: { lnd: {
rest: 8081, rest: 8081,
grpc: 10001, grpc: 10001,
p2p: 9735,
}, },
clightning: { clightning: {
rest: 8181, rest: 8181,
p2p: 9835,
}, },
}; };

19
src/utils/network.spec.ts

@ -49,6 +49,8 @@ describe('Network Utils', () => {
const ports = (await getOpenPorts(network)) as OpenPorts; const ports = (await getOpenPorts(network)) as OpenPorts;
expect(ports).toBeDefined(); expect(ports).toBeDefined();
expect(ports[network.nodes.bitcoin[0].name].rpc).toBe(restPort + 1); expect(ports[network.nodes.bitcoin[0].name].rpc).toBe(restPort + 1);
expect(ports[network.nodes.bitcoin[0].name].zmqBlock).toBeUndefined();
expect(ports[network.nodes.bitcoin[0].name].zmqTx).toBeUndefined();
}); });
it('should update the zmq block port for bitcoind', async () => { it('should update the zmq block port for bitcoind', async () => {
@ -60,7 +62,9 @@ describe('Network Utils', () => {
const zmqBlockPort = network.nodes.bitcoin[0].ports.zmqBlock; const zmqBlockPort = network.nodes.bitcoin[0].ports.zmqBlock;
const ports = (await getOpenPorts(network)) as OpenPorts; const ports = (await getOpenPorts(network)) as OpenPorts;
expect(ports).toBeDefined(); expect(ports).toBeDefined();
expect(ports[network.nodes.bitcoin[0].name].rest).toBeUndefined();
expect(ports[network.nodes.bitcoin[0].name].zmqBlock).toBe(zmqBlockPort + 1); expect(ports[network.nodes.bitcoin[0].name].zmqBlock).toBe(zmqBlockPort + 1);
expect(ports[network.nodes.bitcoin[0].name].zmqTx).toBeUndefined();
}); });
it('should update the zmq tx port for bitcoind', async () => { it('should update the zmq tx port for bitcoind', async () => {
@ -72,6 +76,8 @@ describe('Network Utils', () => {
const zmqTxPort = network.nodes.bitcoin[0].ports.zmqTx; const zmqTxPort = network.nodes.bitcoin[0].ports.zmqTx;
const ports = (await getOpenPorts(network)) as OpenPorts; const ports = (await getOpenPorts(network)) as OpenPorts;
expect(ports).toBeDefined(); expect(ports).toBeDefined();
expect(ports[network.nodes.bitcoin[0].name].rest).toBeUndefined();
expect(ports[network.nodes.bitcoin[0].name].zmqBlock).toBeUndefined();
expect(ports[network.nodes.bitcoin[0].name].zmqTx).toBe(zmqTxPort + 1); expect(ports[network.nodes.bitcoin[0].name].zmqTx).toBe(zmqTxPort + 1);
}); });
@ -99,6 +105,19 @@ describe('Network Utils', () => {
expect(ports[network.nodes.lightning[2].name].rest).toBe(8083); expect(ports[network.nodes.lightning[2].name].rest).toBe(8083);
}); });
it('should update the p2p ports for lightning nodes', async () => {
const portsInUse = [9735, 9836, 9737];
mockDetectPort.mockImplementation(port =>
Promise.resolve(portsInUse.includes(port) ? port + 1 : port),
);
network.nodes.bitcoin = [];
const ports = (await getOpenPorts(network)) as OpenPorts;
expect(ports).toBeDefined();
expect(ports[network.nodes.lightning[0].name].p2p).toBe(9736);
expect(ports[network.nodes.lightning[1].name].p2p).toBe(9837);
expect(ports[network.nodes.lightning[2].name].p2p).toBe(9738);
});
it('should not update ports if none are in use', async () => { it('should not update ports if none are in use', async () => {
const portsInUse: number[] = []; const portsInUse: number[] = [];
mockDetectPort.mockImplementation(port => mockDetectPort.mockImplementation(port =>

29
src/utils/network.ts

@ -103,6 +103,7 @@ export const createLndNetworkNode = (
ports: { ports: {
rest: BasePorts.lnd.rest + id, rest: BasePorts.lnd.rest + id,
grpc: BasePorts.lnd.grpc + id, grpc: BasePorts.lnd.grpc + id,
p2p: BasePorts.lnd.p2p + id,
}, },
}; };
}; };
@ -139,6 +140,7 @@ export const createCLightningNetworkNode = (
}, },
ports: { ports: {
rest: BasePorts.clightning.rest + id, rest: BasePorts.clightning.rest + id,
p2p: BasePorts.clightning.p2p + id,
}, },
}; };
}; };
@ -277,6 +279,7 @@ export interface OpenPorts {
rest?: number; rest?: number;
zmqBlock?: number; zmqBlock?: number;
zmqTx?: number; zmqTx?: number;
p2p?: number;
}; };
} }
@ -345,17 +348,39 @@ export const getOpenPorts = async (network: Network): Promise<OpenPorts | undefi
}; };
}); });
} }
existingPorts = lnd.map(n => n.ports.p2p);
openPorts = await getOpenPortRange(existingPorts);
if (openPorts.join() !== existingPorts.join()) {
openPorts.forEach((port, index) => {
ports[lnd[index].name] = {
...(ports[lnd[index].name] || {}),
p2p: port,
};
});
}
} }
clightning = clightning.filter(n => n.status !== Status.Started); clightning = clightning.filter(n => n.status !== Status.Started);
if (clightning.length) { if (clightning.length) {
const existingPorts = clightning.map(n => n.ports.rest); let existingPorts = clightning.map(n => n.ports.rest);
const openPorts = await getOpenPortRange(existingPorts); let openPorts = await getOpenPortRange(existingPorts);
if (openPorts.join() !== existingPorts.join()) { if (openPorts.join() !== existingPorts.join()) {
openPorts.forEach((port, index) => { openPorts.forEach((port, index) => {
ports[clightning[index].name] = { rest: port }; ports[clightning[index].name] = { rest: port };
}); });
} }
existingPorts = clightning.map(n => n.ports.p2p);
openPorts = await getOpenPortRange(existingPorts);
if (openPorts.join() !== existingPorts.join()) {
openPorts.forEach((port, index) => {
ports[clightning[index].name] = {
...(ports[clightning[index].name] || {}),
p2p: port,
};
});
}
} }
// return undefined if no ports where updated // return undefined if no ports where updated

Loading…
Cancel
Save