From 20d77ebc7032db935a3aa90a7af62fd4340e1861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hampus=20Sj=C3=B6berg?= Date: Fri, 21 Feb 2020 16:34:58 +0100 Subject: [PATCH] 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). --- .../designer/lightning/ConnectTab.tsx | 15 ++++++++-- src/i18n/locales/en-US.json | 3 +- src/lib/docker/composeFile.ts | 23 ++++++++++++--- src/lib/docker/nodeTemplates.spec.ts | 1 + src/lib/docker/nodeTemplates.ts | 4 +++ src/shared/types.ts | 2 ++ src/utils/constants.ts | 2 ++ src/utils/network.spec.ts | 19 ++++++++++++ src/utils/network.ts | 29 +++++++++++++++++-- 9 files changed, 88 insertions(+), 10 deletions(-) diff --git a/src/components/designer/lightning/ConnectTab.tsx b/src/components/designer/lightning/ConnectTab.tsx index 5bde518..0391068 100644 --- a/src/components/designer/lightning/ConnectTab.tsx +++ b/src/components/designer/lightning/ConnectTab.tsx @@ -40,6 +40,7 @@ export interface ConnectionInfo { readOnly?: string; cert?: string; }; + p2pUriExternal: string; } interface Props { @@ -51,8 +52,9 @@ const ConnectTab: React.FC = ({ node }) => { const [authType, setAuthType] = useState('paths'); const { openInBrowser } = useStoreActions(s => s.app); 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 => { if (node.status === Status.Started) { if (node.implementation === 'LND') { @@ -67,6 +69,7 @@ const ConnectTab: React.FC = ({ node }) => { readOnly: lnd.paths.readonlyMacaroon, cert: lnd.paths.tlsCert, }, + p2pUriExternal: `${pubkey}@127.0.0.1:${lnd.ports.p2p}`, }; } else if (node.implementation === 'c-lightning') { const cln = node as CLightningNode; @@ -76,6 +79,7 @@ const ConnectTab: React.FC = ({ node }) => { credentials: { admin: cln.paths.macaroon, }, + p2pUriExternal: `${pubkey}@127.0.0.1:${cln.ports.p2p}`, }; } } @@ -85,7 +89,7 @@ const ConnectTab: React.FC = ({ node }) => { restDocsUrl: '', credentials: {}, } as ConnectionInfo; - }, [node]); + }, [node, pubkey]); if (node.status !== Status.Started) { return <>{l('notStarted')}; @@ -95,7 +99,12 @@ const ConnectTab: React.FC = ({ node }) => { const hosts: DetailValues = [ [l('grpcHost'), grpcUrl, grpcUrl], [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 .map(([label, value, text]) => ({ diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index 3a83212..13daa46 100644 --- a/src/i18n/locales/en-US.json +++ b/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.grpcHost": "GRPC 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.filePaths": "File Paths", "cmps.designer.lightning.ConnectTab.hexStrings": "HEX", diff --git a/src/lib/docker/composeFile.ts b/src/lib/docker/composeFile.ts index 8387f2d..74bf8ba 100644 --- a/src/lib/docker/composeFile.ts +++ b/src/lib/docker/composeFile.ts @@ -49,22 +49,37 @@ class ComposeFile { const { name, version, - ports: { rest, grpc }, + ports: { rest, grpc, p2p }, } = node; const container = getContainerName(node); 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) { const { name, version, - ports: { rest }, + ports: { rest, p2p }, } = node; const container = getContainerName(node); 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, + ); } } diff --git a/src/lib/docker/nodeTemplates.spec.ts b/src/lib/docker/nodeTemplates.spec.ts index e31cdb3..b0c553c 100644 --- a/src/lib/docker/nodeTemplates.spec.ts +++ b/src/lib/docker/nodeTemplates.spec.ts @@ -17,6 +17,7 @@ describe('nodeTemplates', () => { 'btcnode1', 8080, 10009, + 9735, ); expect(node.image).toContain('lnd'); expect(node.container_name).toEqual('polar-mynode'); diff --git a/src/lib/docker/nodeTemplates.ts b/src/lib/docker/nodeTemplates.ts index 649ac1e..8c054ee 100644 --- a/src/lib/docker/nodeTemplates.ts +++ b/src/lib/docker/nodeTemplates.ts @@ -62,6 +62,7 @@ export const lnd = ( backendName: string, restPort: number, grpcPort: number, + p2pPort: number, ): ComposeService => ({ image: `polarlightning/lnd:${version}`, container_name: container, @@ -99,6 +100,7 @@ export const lnd = ( ports: [ `${restPort}:8080`, // REST `${grpcPort}:10009`, // gRPC + `${p2pPort}:9735`, // p2p ], }); @@ -108,6 +110,7 @@ export const clightning = ( version: string, backendName: string, restPort: number, + p2pPort: number, ): ComposeService => ({ image: `polarlightning/clightning:${version}`, container_name: container, @@ -143,5 +146,6 @@ export const clightning = ( ], ports: [ `${restPort}:8080`, // REST + `${p2pPort}:9735`, // p2p ], }); diff --git a/src/shared/types.ts b/src/shared/types.ts index 00eb7e9..327e577 100644 --- a/src/shared/types.ts +++ b/src/shared/types.ts @@ -33,6 +33,7 @@ export interface LndNode extends LightningNode { ports: { rest: number; grpc: number; + p2p: number; }; } @@ -42,6 +43,7 @@ export interface CLightningNode extends LightningNode { }; ports: { rest: number; + p2p: number; }; } diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 4920e5a..21135e7 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -51,9 +51,11 @@ export const BasePorts = { lnd: { rest: 8081, grpc: 10001, + p2p: 9735, }, clightning: { rest: 8181, + p2p: 9835, }, }; diff --git a/src/utils/network.spec.ts b/src/utils/network.spec.ts index f8afe52..2b219b5 100644 --- a/src/utils/network.spec.ts +++ b/src/utils/network.spec.ts @@ -49,6 +49,8 @@ describe('Network Utils', () => { const ports = (await getOpenPorts(network)) as OpenPorts; expect(ports).toBeDefined(); 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 () => { @@ -60,7 +62,9 @@ describe('Network Utils', () => { const zmqBlockPort = network.nodes.bitcoin[0].ports.zmqBlock; const ports = (await getOpenPorts(network)) as OpenPorts; 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].zmqTx).toBeUndefined(); }); 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 ports = (await getOpenPorts(network)) as OpenPorts; 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); }); @@ -99,6 +105,19 @@ describe('Network Utils', () => { 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 () => { const portsInUse: number[] = []; mockDetectPort.mockImplementation(port => diff --git a/src/utils/network.ts b/src/utils/network.ts index 5f418ae..f880a8d 100644 --- a/src/utils/network.ts +++ b/src/utils/network.ts @@ -103,6 +103,7 @@ export const createLndNetworkNode = ( ports: { rest: BasePorts.lnd.rest + id, grpc: BasePorts.lnd.grpc + id, + p2p: BasePorts.lnd.p2p + id, }, }; }; @@ -139,6 +140,7 @@ export const createCLightningNetworkNode = ( }, ports: { rest: BasePorts.clightning.rest + id, + p2p: BasePorts.clightning.p2p + id, }, }; }; @@ -277,6 +279,7 @@ export interface OpenPorts { rest?: number; zmqBlock?: number; zmqTx?: number; + p2p?: number; }; } @@ -345,17 +348,39 @@ export const getOpenPorts = async (network: Network): Promise 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); if (clightning.length) { - const existingPorts = clightning.map(n => n.ports.rest); - const openPorts = await getOpenPortRange(existingPorts); + let existingPorts = clightning.map(n => n.ports.rest); + let openPorts = await getOpenPortRange(existingPorts); if (openPorts.join() !== existingPorts.join()) { openPorts.forEach((port, index) => { 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