diff --git a/TODO.md b/TODO.md index 550ed7b..6fbb71c 100644 --- a/TODO.md +++ b/TODO.md @@ -2,11 +2,15 @@ Small Stuff -- dont allow open channel if both nodes aren't Started +- add draggable c-lightning node to sidebar +- in designer.ts change addLndNode to addLightningNode +- in network.ts rename addLndNode to addNode +- in network.ts update removeNode for c-lightning +- rename lnd in locale files + - implement real-time channel updates from LND via GRPC streams - implement option to auto-mine every X minutes - switch renovatebot to dependabot and use automatic security fixes -- generate alice/bob/carol names for lnd nodes - display docker streaming logs in the UI - mock or install docker on build servers for e2e tests - consistent scrollbars for all OS's (https://github.com/xobotyi/react-scrollbars-custom) (https://github.com/souhe/reactScrollbar) @@ -16,7 +20,7 @@ Bigger things - create a splash page website for lightningpolar.com - add block explorer (https://github.com/janoside/btc-rpc-explorer) - add grpc API explorer (https://github.com/grpc-ecosystem/awesome-grpc#gui) -- add support for c-lightning, eclair, btcd +- add support for eclair, btcd - live theme changer (https://medium.com/@mzohaib.qc/ant-design-dynamic-runtime-theme-1f9a1a030ba0) - dark theme UI (https://material.io/design/color/dark-theme.html) - POC testing-library for testcafe (https://testing-library.com/docs/testcafe-testing-library/intro) diff --git a/src/components/designer/NetworkDesigner.spec.tsx b/src/components/designer/NetworkDesigner.spec.tsx index 5a4751f..a63c8f8 100644 --- a/src/components/designer/NetworkDesigner.spec.tsx +++ b/src/components/designer/NetworkDesigner.spec.tsx @@ -46,7 +46,7 @@ describe('NetworkDesigner Component', () => { expect(await findByText('backend')).toBeInTheDocument(); }); - it('should render correct # of LND nodes', async () => { + it('should render correct # of lightning nodes', async () => { const { findByText } = renderComponent(); expect(await findByText('alice')).toBeInTheDocument(); expect(await findByText('bob')).toBeInTheDocument(); diff --git a/src/components/designer/lightning/LightningDetails.spec.tsx b/src/components/designer/lightning/LightningDetails.spec.tsx index 74ed497..d9883b8 100644 --- a/src/components/designer/lightning/LightningDetails.spec.tsx +++ b/src/components/designer/lightning/LightningDetails.spec.tsx @@ -3,7 +3,6 @@ import { shell } from 'electron'; import { fireEvent, wait, waitForElement } from '@testing-library/dom'; import { Status } from 'shared/types'; import * as files from 'utils/files'; -import { groupNodes } from 'utils/network'; import { defaultStateBalances, defaultStateInfo, @@ -15,7 +14,7 @@ import LightningDetails from './LightningDetails'; jest.mock('utils/files'); -describe('LndDetails', () => { +describe('LightningDetails', () => { const renderComponent = (status?: Status) => { const network = getNetwork(1, 'test network', status); if (status === Status.Error) { @@ -31,7 +30,7 @@ describe('LndDetails', () => { }, }, }; - const node = groupNodes(network).lnd[0]; + const node = network.nodes.lightning[0]; const cmp = ; const result = renderWithProviders(cmp, { initialState }); return { @@ -185,7 +184,7 @@ describe('LndDetails', () => { expect(queryByText('Unconfirmed Balance')).not.toBeInTheDocument(); }); - it('should not display LND info if its undefined', async () => { + it('should not display node info if its undefined', async () => { lightningServiceMock.getInfo.mockResolvedValue(null as any); const { getByText, queryByText, findByText } = renderComponent(Status.Started); fireEvent.click(await findByText('Info')); diff --git a/src/components/designer/lightning/actions/Deposit.spec.tsx b/src/components/designer/lightning/actions/Deposit.spec.tsx index 4645c05..37ecf01 100644 --- a/src/components/designer/lightning/actions/Deposit.spec.tsx +++ b/src/components/designer/lightning/actions/Deposit.spec.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { fireEvent, waitForElement } from '@testing-library/dom'; import { BitcoindLibrary } from 'types'; -import { groupNodes } from 'utils/network'; import { defaultStateInfo, getNetwork, @@ -21,7 +20,7 @@ describe('Deposit', () => { networks: [network], }, }; - const node = groupNodes(network).lnd[0]; + const node = network.nodes.lightning[0]; const cmp = ; const result = renderWithProviders(cmp, { initialState }); return { diff --git a/src/components/designer/lightning/actions/Deposit.tsx b/src/components/designer/lightning/actions/Deposit.tsx index 08195bc..cd02ff9 100644 --- a/src/components/designer/lightning/actions/Deposit.tsx +++ b/src/components/designer/lightning/actions/Deposit.tsx @@ -8,7 +8,7 @@ import { format } from 'utils/units'; const InputGroup = Input.Group; -const LndDeposit: React.FC<{ node: LightningNode }> = ({ node }) => { +const Deposit: React.FC<{ node: LightningNode }> = ({ node }) => { const { l } = usePrefixedTranslation('cmps.designer.lnd.actions.Deposit'); const [amount, setAmount] = useState(1000000); const { notify } = useStoreActions(s => s.app); @@ -49,4 +49,4 @@ const LndDeposit: React.FC<{ node: LightningNode }> = ({ node }) => { ); }; -export default LndDeposit; +export default Deposit; diff --git a/src/components/designer/lightning/actions/RemoveNode.spec.tsx b/src/components/designer/lightning/actions/RemoveNode.spec.tsx index 4f8a2ac..3edf3a4 100644 --- a/src/components/designer/lightning/actions/RemoveNode.spec.tsx +++ b/src/components/designer/lightning/actions/RemoveNode.spec.tsx @@ -3,7 +3,6 @@ import { fireEvent, waitForElement } from '@testing-library/dom'; import { Status } from 'shared/types'; import { DockerLibrary } from 'types'; import { initChartFromNetwork } from 'utils/chart'; -import { groupNodes } from 'utils/network'; import { getNetwork, injections, @@ -32,8 +31,8 @@ describe('RemoveNode', () => { activeId: 1, }, }; - const { lnd } = groupNodes(network); - const node = lnd[status === Status.Started ? 0 : 1]; + const { lightning } = network.nodes; + const node = lightning[status === Status.Started ? 0 : 1]; const cmp = ; const result = renderWithProviders(cmp, { initialState }); return { diff --git a/src/components/designer/link/Backend.spec.tsx b/src/components/designer/link/Backend.spec.tsx index 7b80454..7772f49 100644 --- a/src/components/designer/link/Backend.spec.tsx +++ b/src/components/designer/link/Backend.spec.tsx @@ -8,37 +8,37 @@ describe('Backend component', () => { const renderComponent = () => { const network = getNetwork(); const bitcoind = network.nodes.bitcoin[0]; - const lnd = network.nodes.lightning[0]; - const result = render(); + const lightning = network.nodes.lightning[0]; + const result = render(); return { ...result, bitcoind, - lnd, + lightning, }; }; - describe('LND Details', () => { + describe('Lightning Details', () => { it('should display Name', () => { - const { getByText, lnd } = renderComponent(); - expect(getByText(lnd.name)).toBeInTheDocument(); + const { getByText, lightning } = renderComponent(); + expect(getByText(lightning.name)).toBeInTheDocument(); }); it('should display Implementation', () => { - const { getByText, getAllByText, lnd } = renderComponent(); + const { getByText, getAllByText, lightning } = renderComponent(); expect(getAllByText('Implementation')).toHaveLength(2); - expect(getByText(lnd.implementation)).toBeInTheDocument(); + expect(getByText(lightning.implementation)).toBeInTheDocument(); }); it('should display Version', () => { - const { getByText, getAllByText, lnd } = renderComponent(); + const { getByText, getAllByText, lightning } = renderComponent(); expect(getAllByText('Version')).toHaveLength(2); - expect(getByText(`v${lnd.version}`)).toBeInTheDocument(); + expect(getByText(`v${lightning.version}`)).toBeInTheDocument(); }); it('should display Status', () => { - const { getAllByText, lnd } = renderComponent(); + const { getAllByText, lightning } = renderComponent(); expect(getAllByText('Status')).toHaveLength(2); - expect(getAllByText(Status[lnd.status])).toHaveLength(2); + expect(getAllByText(Status[lightning.status])).toHaveLength(2); }); }); diff --git a/src/components/designer/link/Channel.tsx b/src/components/designer/link/Channel.tsx index d683aac..c50c3b2 100644 --- a/src/components/designer/link/Channel.tsx +++ b/src/components/designer/link/Channel.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { ILink } from '@mrblenny/react-flow-chart'; import { Button, Modal } from 'antd'; import { usePrefixedTranslation } from 'hooks'; -import { LightningNode, LndNode, Status } from 'shared/types'; +import { LightningNode, Status } from 'shared/types'; import { useStoreActions } from 'store'; import { LinkProperties } from 'utils/chart'; import { ellipseInner } from 'utils/strings'; @@ -38,7 +38,7 @@ const Channel: React.FC = ({ link, from, to }) => { cancelText: l('closeChanCancelBtn'), onOk: async () => { try { - await closeChannel({ node: from as LndNode, channelPoint }); + await closeChannel({ node: from, channelPoint }); notify({ message: l('closeChanSuccess') }); } catch (error) { notify({ message: l('closeChanError'), error }); diff --git a/src/components/terminal/OpenTerminalButton.spec.tsx b/src/components/terminal/OpenTerminalButton.spec.tsx index b3c6123..007bdc9 100644 --- a/src/components/terminal/OpenTerminalButton.spec.tsx +++ b/src/components/terminal/OpenTerminalButton.spec.tsx @@ -1,14 +1,13 @@ import React from 'react'; import { fireEvent, wait } from '@testing-library/dom'; import { ipcChannels } from 'shared'; -import { BitcoinNode, LndNode } from 'shared/types'; +import { BitcoinNode, LightningNode } from 'shared/types'; import { Network } from 'types'; -import { groupNodes } from 'utils/network'; import { getNetwork, injections, renderWithProviders } from 'utils/tests'; import OpenTerminalButton from './OpenTerminalButton'; describe('OpenTerminalButton', () => { - const renderComponent = (nodeSelector: (n: Network) => LndNode | BitcoinNode) => { + const renderComponent = (nodeSelector: (n: Network) => LightningNode | BitcoinNode) => { const network = getNetwork(1, 'test network'); return renderWithProviders(); }; @@ -30,7 +29,7 @@ describe('OpenTerminalButton', () => { }); it('should render lnd help text', () => { - const { getByText } = renderComponent(n => groupNodes(n).lnd[0]); + const { getByText } = renderComponent(n => n.nodes.lightning[0]); const help = getByText("Run 'lncli' commands directly on the node"); expect(help).toBeInTheDocument(); }); diff --git a/src/components/terminal/OpenTerminalButton.tsx b/src/components/terminal/OpenTerminalButton.tsx index 48b13cc..b7bccce 100644 --- a/src/components/terminal/OpenTerminalButton.tsx +++ b/src/components/terminal/OpenTerminalButton.tsx @@ -21,6 +21,9 @@ const OpenTerminalButton: React.FC = ({ node }) => { case 'LND': cmd = 'lncli'; break; + case 'c-lightning': + cmd = 'lightning-cli'; + break; case 'bitcoind': cmd = 'bitcoin-cli'; break; diff --git a/src/lib/docker/composeFile.spec.ts b/src/lib/docker/composeFile.spec.ts index 3551cb6..8b1d61b 100644 --- a/src/lib/docker/composeFile.spec.ts +++ b/src/lib/docker/composeFile.spec.ts @@ -1,4 +1,4 @@ -import { groupNodes } from 'utils/network'; +import { LndNode } from 'shared/types'; import { getNetwork } from 'utils/tests'; import ComposeFile from './composeFile'; @@ -6,7 +6,7 @@ describe('ComposeFile', () => { let composeFile = new ComposeFile(); const network = getNetwork(); const btcNode = network.nodes.bitcoin[0]; - const lndNode = groupNodes(network).lnd[0]; + const lndNode = network.nodes.lightning[0] as LndNode; beforeEach(() => { composeFile = new ComposeFile(); diff --git a/src/lib/docker/dockerService.spec.ts b/src/lib/docker/dockerService.spec.ts index 078dd23..ee8de38 100644 --- a/src/lib/docker/dockerService.spec.ts +++ b/src/lib/docker/dockerService.spec.ts @@ -235,13 +235,6 @@ describe('DockerService', () => { expect(fs.ensureDir).toBeCalledTimes(3); }); - it('should not create volume dirs for unknown implementations', async () => { - network.nodes.lightning[0].implementation = 'c-lightning'; - composeMock.upAll.mockResolvedValue(mockResult); - await dockerService.start(network); - expect(fs.ensureDir).toBeCalledTimes(2); - }); - it('should call compose.down when a network is stopped', async () => { composeMock.down.mockResolvedValue(mockResult); await dockerService.stop(network); diff --git a/src/lib/docker/dockerService.ts b/src/lib/docker/dockerService.ts index ead375a..f4ad1c9 100644 --- a/src/lib/docker/dockerService.ts +++ b/src/lib/docker/dockerService.ts @@ -109,9 +109,8 @@ class DockerService implements DockerLibrary { await ensureDir(join(network.path, 'volumes', 'bitcoind', node.name)); } for (const node of lightning) { - if (node.implementation === 'LND') { - await ensureDir(join(network.path, 'volumes', 'lnd', node.name)); - } + const volDir = node.implementation.toLocaleLowerCase().replace('-', ''); + await ensureDir(join(network.path, 'volumes', volDir, node.name)); } info(`Starting docker containers for ${network.name}`); diff --git a/src/lib/lightning/lnd/lndProxyClient.spec.ts b/src/lib/lightning/lnd/lndProxyClient.spec.ts index 4457bc6..9f1f52a 100644 --- a/src/lib/lightning/lnd/lndProxyClient.spec.ts +++ b/src/lib/lightning/lnd/lndProxyClient.spec.ts @@ -1,11 +1,11 @@ import { ipcChannels } from 'shared'; +import { LndNode } from 'shared/types'; import { IpcSender } from 'lib/ipc/ipcService'; -import { groupNodes } from 'utils/network'; import { getNetwork } from 'utils/tests'; import lndProxyClient from './lndProxyClient'; describe('LndService', () => { - const node = groupNodes(getNetwork()).lnd[0]; + const node = getNetwork().nodes.lightning[0] as LndNode; let actualIpc: IpcSender; beforeEach(() => { diff --git a/src/lib/lightning/lnd/lndService.spec.ts b/src/lib/lightning/lnd/lndService.spec.ts index c379591..9eaac4e 100644 --- a/src/lib/lightning/lnd/lndService.spec.ts +++ b/src/lib/lightning/lnd/lndService.spec.ts @@ -7,7 +7,6 @@ import { defaultPendingOpenChannel, defaultWalletBalance, } from 'shared'; -import { groupNodes } from 'utils/network'; import { defaultStateBalances, defaultStateInfo, getNetwork } from 'utils/tests'; import lndProxyClient from './lndProxyClient'; import lndService from './lndService'; @@ -15,7 +14,7 @@ import lndService from './lndService'; jest.mock('./lndProxyClient'); describe('LndService', () => { - const node = groupNodes(getNetwork()).lnd[0]; + const node = getNetwork().nodes.lightning[0]; it('should get node info', async () => { const apiResponse = defaultInfo({ identityPubkey: 'asdf' }); diff --git a/src/store/models/designer.ts b/src/store/models/designer.ts index a1e74c1..48fe342 100644 --- a/src/store/models/designer.ts +++ b/src/store/models/designer.ts @@ -13,7 +13,12 @@ import { } from 'easy-peasy'; import { LndNode, Status } from 'shared/types'; import { Network, StoreInjections } from 'types'; -import { createLightningChartNode, rotate, snap, updateChartFromLnd } from 'utils/chart'; +import { + createLightningChartNode, + rotate, + snap, + updateChartFromNodes, +} from 'utils/chart'; import { LOADING_NODE_ID } from 'utils/constants'; import { prefixTranslation } from 'utils/translate'; import { RootModel } from './'; @@ -104,7 +109,7 @@ const designerModel: DesignerModel = { const nodesData = getStoreState().lightning.nodes; const { allCharts } = getState(); // sync the chart with data from all of the nodes - const chart = updateChartFromLnd(allCharts[network.id], nodesData); + const chart = updateChartFromNodes(allCharts[network.id], nodesData); actions.setAllCharts({ ...allCharts, [network.id]: chart, diff --git a/src/store/models/lightning.spec.ts b/src/store/models/lightning.spec.ts index 127ce93..4fea243 100644 --- a/src/store/models/lightning.spec.ts +++ b/src/store/models/lightning.spec.ts @@ -20,7 +20,7 @@ jest.mock('utils/async'); const asyncUtilMock = asyncUtil as jest.Mocked; const bitcoindServiceMock = injections.bitcoindService as jest.Mocked; -describe('LND Model', () => { +describe('Lightning Model', () => { const rootModel = { network: networkModel, lightning: lightningModel, diff --git a/src/store/models/lightning.ts b/src/store/models/lightning.ts index 6cae3a9..0d51495 100644 --- a/src/store/models/lightning.ts +++ b/src/store/models/lightning.ts @@ -129,7 +129,7 @@ const lightningModel: LightningModel = { // cast because it should never be undefined after calling getInfo above const { rpcUrl } = getStoreState().lightning.nodes[to.name] .info as LightningNodeInfo; - // open the channel via LND + // open the channel via lightning node const api = injections.lightningFactory.getService(from); await api.openChannel(from, rpcUrl, sats); // mine some blocks to confirm the txn diff --git a/src/store/models/network.spec.ts b/src/store/models/network.spec.ts index ca3c5ce..617cde3 100644 --- a/src/store/models/network.spec.ts +++ b/src/store/models/network.spec.ts @@ -97,7 +97,7 @@ describe('Network model', () => { expect(injections.dockerService.saveComposeFile).toBeCalledTimes(1); }); - it('should add a network with the correct LND nodes', async () => { + it('should add a network with the correct lightning nodes', async () => { await store.getActions().network.addNetwork(addNetworkArgs); const { lightning } = firstNetwork().nodes; expect(lightning.length).toBe(2); @@ -240,7 +240,7 @@ describe('Network model', () => { ); }); - it('should set LND node status to error if the node startup fails', async () => { + it('should set lightning node status to error if the node startup fails', async () => { lightningServiceMock.waitUntilOnline.mockRejectedValue(new Error('test-error')); const { start } = store.getActions().network; const network = firstNetwork(); diff --git a/src/store/models/network.ts b/src/store/models/network.ts index 15900cb..8979f4b 100644 --- a/src/store/models/network.ts +++ b/src/store/models/network.ts @@ -277,6 +277,7 @@ const networkModel: NetworkModel = { const newNetworks = networks.filter(n => n.id !== networkId); actions.setNetworks(newNetworks); getStoreActions().designer.removeChart(networkId); + network.nodes.lightning.forEach(n => getStoreActions().lightning.removeNode(n.name)); await actions.save(); await getStoreActions().app.clearAppCache(); }), diff --git a/src/utils/chart.spec.ts b/src/utils/chart.spec.ts index dc418cb..f4b1063 100644 --- a/src/utils/chart.spec.ts +++ b/src/utils/chart.spec.ts @@ -2,15 +2,15 @@ import { IChart, IConfig } from '@mrblenny/react-flow-chart'; import { LightningNodeMapping } from 'store/models/lightning'; import { Network } from 'types'; import { defaultStateChannel, defaultStateInfo, getNetwork } from 'utils/tests'; -import { initChartFromNetwork, snap, updateChartFromLnd } from './chart'; +import { initChartFromNetwork, snap, updateChartFromNodes } from './chart'; describe('Chart Util', () => { let network: Network; let chart: IChart; - let lndData: LightningNodeMapping; + let nodesData: LightningNodeMapping; const addChannel = (node: string, remotePubkey: string, pending?: boolean) => { - const { channels } = lndData[node]; + const { channels } = nodesData[node]; if (channels) { channels.push( defaultStateChannel({ @@ -30,13 +30,13 @@ describe('Chart Util', () => { beforeEach(() => { network = getNetwork(); chart = initChartFromNetwork(network); - lndData = { + nodesData = { [network.nodes.lightning[0].name]: { - info: defaultStateInfo({ pubkey: 'lnd1pubkey' }), + info: defaultStateInfo({ pubkey: 'ln1pubkey' }), channels: [], }, [network.nodes.lightning[1].name]: { - info: defaultStateInfo({ pubkey: 'lnd2pubkey' }), + info: defaultStateInfo({ pubkey: 'ln2pubkey' }), channels: [], }, }; @@ -61,8 +61,8 @@ describe('Chart Util', () => { describe('updateChartFromNetwork', () => { it('should create link for an open channel', () => { - addChannel('alice', 'lnd2pubkey'); - const result = updateChartFromLnd(chart, lndData); + addChannel('alice', 'ln2pubkey'); + const result = updateChartFromNodes(chart, nodesData); expect(result.links['xxxxxxxxxx:0']).toBeDefined(); const link = result.links['xxxxxxxxxx:0']; expect(link.from.nodeId).toBe('alice'); @@ -73,8 +73,8 @@ describe('Chart Util', () => { }); it('should create link for a pending channel', () => { - addChannel('alice', 'lnd2pubkey', true); - const result = updateChartFromLnd(chart, lndData); + addChannel('alice', 'ln2pubkey', true); + const result = updateChartFromNodes(chart, nodesData); expect(result.links['xxxxxxxxxx:0']).toBeDefined(); const link = result.links['xxxxxxxxxx:0']; expect(link.from.nodeId).toBe('alice'); @@ -85,28 +85,28 @@ describe('Chart Util', () => { }); it('should remove links for channels that do not exist', () => { - addChannel('alice', 'lnd2pubkey'); - const result = updateChartFromLnd(chart, lndData); + addChannel('alice', 'ln2pubkey'); + const result = updateChartFromNodes(chart, nodesData); expect(result.links['xxxxxxxxxx:0']).toBeTruthy(); // remove the channel - const node = lndData['alice']; + const node = nodesData['alice']; if (node.channels) node.channels = []; - const result2 = updateChartFromLnd(result, lndData); + const result2 = updateChartFromNodes(result, nodesData); expect(result2.links['xxxxxxxxxx:0']).toBeUndefined(); }); it('should make no changes if channels is undefined', () => { - lndData['alice'].channels = undefined; - lndData['bob'].channels = undefined; - const result = updateChartFromLnd(chart, lndData); + nodesData['alice'].channels = undefined; + nodesData['bob'].channels = undefined; + const result = updateChartFromNodes(chart, nodesData); expect(result).toEqual(chart); }); it('should point link right to left', () => { chart.nodes['alice'].position.x = 200; chart.nodes['bob'].position.x = 100; - addChannel('alice', 'lnd2pubkey'); - const result = updateChartFromLnd(chart, lndData); + addChannel('alice', 'ln2pubkey'); + const result = updateChartFromNodes(chart, nodesData); const link = result.links['xxxxxxxxxx:0']; expect(link.properties.direction).toEqual('rtl'); }); @@ -114,8 +114,8 @@ describe('Chart Util', () => { it('should update the node sizes', () => { chart.nodes['alice'].size = { width: 100, height: 20 }; chart.nodes['bob'].size = undefined; - addChannel('alice', 'lnd2pubkey'); - const result = updateChartFromLnd(chart, lndData); + addChannel('alice', 'ln2pubkey'); + const result = updateChartFromNodes(chart, nodesData); let size = result.nodes['alice'].size; expect(size).toBeDefined(); if (size) expect(size.height).toBe(60); diff --git a/src/utils/chart.ts b/src/utils/chart.ts index 62cb987..f9b4d88 100644 --- a/src/utils/chart.ts +++ b/src/utils/chart.ts @@ -162,13 +162,13 @@ const updateLinksAndPorts = ( }; }; -export const updateChartFromLnd = ( +export const updateChartFromNodes = ( chart: IChart, - lndData: LightningNodeMapping, + nodesData: LightningNodeMapping, ): IChart => { // create a mapping of node name to pubkey for lookups const pubkeys: Record = {}; - Object.entries(lndData).forEach(([name, data]) => { + Object.entries(nodesData).forEach(([name, data]) => { if (!data.info || !data.info.pubkey) return; pubkeys[data.info.pubkey] = name; }); @@ -178,7 +178,7 @@ export const updateChartFromLnd = ( const createdLinkIds: string[] = []; // update the node and links for each node - Object.entries(lndData).forEach(([fromName, data]) => { + Object.entries(nodesData).forEach(([fromName, data]) => { const fromNode = nodes[fromName]; if (data.channels) { @@ -208,7 +208,6 @@ export const updateChartFromLnd = ( // remove ports for channels that no longer exist Object.values(nodes).forEach(node => { - if (!node) return; Object.keys(node.ports).forEach(portId => { // don't remove special ports if (['empty-left', 'empty-right', 'backend'].includes(portId)) return; @@ -220,7 +219,7 @@ export const updateChartFromLnd = ( }); // resize chart nodes if necessary to fit new ports - Object.keys(lndData).forEach(name => updateNodeSize(nodes[name])); + Object.keys(nodesData).forEach(name => updateNodeSize(nodes[name])); return { ...chart, diff --git a/src/utils/network.ts b/src/utils/network.ts index 4b20956..120718f 100644 --- a/src/utils/network.ts +++ b/src/utils/network.ts @@ -18,7 +18,7 @@ import { range } from './numbers'; export const getContainerName = (node: CommonNode) => `polar-n${node.networkId}-${node.name}`; -export const groupNodes = (network: Network) => { +const groupNodes = (network: Network) => { const { bitcoin, lightning } = network.nodes; return { bitcoind: bitcoin.filter(n => n.implementation === 'bitcoind') as BitcoinNode[],