Browse Source

feat(bitcoind): drag & drop new bitcoind nodes onto the network

master
jamaljsr 5 years ago
parent
commit
593fa5ecb2
  1. 2
      TODO.md
  2. 13
      src/components/designer/default/DefaultSidebar.tsx
  3. 5
      src/shared/types.ts
  4. 6
      src/store/models/bitcoind.ts
  5. 23
      src/store/models/designer.ts
  6. 34
      src/store/models/network.ts
  7. 11
      src/utils/chart.ts
  8. 24
      src/utils/network.ts

2
TODO.md

@ -1,7 +1,5 @@
# TODO List
- add peer nodes after waitUntilOnline()
Small Stuff
- fix pasting text in terminal

13
src/components/designer/default/DefaultSidebar.tsx

@ -1,8 +1,9 @@
import React from 'react';
import styled from '@emotion/styled';
import { usePrefixedTranslation } from 'hooks';
import { CLightningVersion, LndVersion } from 'shared/types';
import { BitcoindVersion, CLightningVersion, LndVersion } from 'shared/types';
import { Network } from 'types';
import bitcoindLogo from 'resources/bitcoin.svg';
import clightningLogo from 'resources/clightning.png';
import lndLogo from 'resources/lnd.png';
import SidebarCard from '../SidebarCard';
@ -51,6 +52,16 @@ const DefaultSidebar: React.FC<Props> = ({ network }) => {
properties={{ type: 'c-lightning', version }}
/>
))}
{Object.keys(BitcoindVersion)
.filter(v => v !== 'latest')
.map(version => (
<DraggableNode
key={version}
label={`Bitcoin Core v${version}`}
icon={bitcoindLogo}
properties={{ type: 'bitcoind', version }}
/>
))}
</SidebarCard>
);
};

5
src/shared/types.ts

@ -35,6 +35,11 @@ export enum CLightningVersion {
'0.7.3' = '0.7.3',
}
export enum BitcoindVersion {
latest = '0.18.1',
'0.18.1' = '0.18.1',
}
export interface LndNode extends LightningNode {
paths: {
tlsCert: string;

6
src/store/models/bitcoind.ts

@ -17,6 +17,7 @@ export interface BitcoindNodeModel {
export interface BitcoindModel {
nodes: BitcoindNodeMapping;
removeNode: Action<BitcoindModel, string>;
setChainInfo: Action<BitcoindModel, { node: BitcoinNode; chainInfo: ChainInfo }>;
setWalletinfo: Action<BitcoindModel, { node: BitcoinNode; walletInfo: WalletInfo }>;
getInfo: Thunk<BitcoindModel, BitcoinNode, StoreInjections>;
@ -27,6 +28,11 @@ const bitcoindModel: BitcoindModel = {
// computed properties/functions
nodes: {},
// reducer actions (mutations allowed thx to immer)
removeNode: action((state, name) => {
if (state.nodes[name]) {
delete state.nodes[name];
}
}),
setChainInfo: action((state, { node, chainInfo }) => {
if (!state.nodes[node.name]) state.nodes[node.name] = {};
state.nodes[node.name].chainInfo = chainInfo;

23
src/store/models/designer.ts

@ -11,9 +11,10 @@ import {
ThunkOn,
thunkOn,
} from 'easy-peasy';
import { LightningNode, Status } from 'shared/types';
import { BitcoinNode, LightningNode, Status } from 'shared/types';
import { Network, StoreInjections } from 'types';
import {
createBitcoinChartNode,
createLightningChartNode,
rotate,
snap,
@ -39,7 +40,10 @@ export interface DesignerModel {
onNetworkSetStatus: ActionOn<DesignerModel, RootModel>;
removeLink: Action<DesignerModel, string>;
removeNode: Action<DesignerModel, string>;
addNode: Action<DesignerModel, { lnNode: LightningNode; position: IPosition }>;
addNode: Action<
DesignerModel,
{ newNode: LightningNode | BitcoinNode; position: IPosition }
>;
onLinkCompleteListener: ThunkOn<DesignerModel, StoreInjections, RootModel>;
onCanvasDropListener: ThunkOn<DesignerModel, StoreInjections, RootModel>;
// Flowchart component callbacks
@ -151,12 +155,15 @@ const designerModel: DesignerModel = {
}
});
}),
addNode: action((state, { lnNode, position }) => {
addNode: action((state, { newNode, position }) => {
const chart = state.allCharts[state.activeId];
const { node, link } = createLightningChartNode(lnNode);
const { node, link } =
newNode.type === 'lightning'
? createLightningChartNode(newNode)
: createBitcoinChartNode(newNode);
node.position = position;
chart.nodes[node.id] = node;
chart.links[link.id] = link;
if (link) chart.links[link.id] = link;
}),
onLinkCompleteListener: thunkOn(
actions => actions.onLinkComplete,
@ -210,14 +217,14 @@ const designerModel: DesignerModel = {
message: l('dropErrTitle'),
error: new Error(l('dropErrMsg')),
});
} else if (['lnd', 'c-lightning'].includes(data.type)) {
} else if (['lnd', 'c-lightning', 'bitcoind'].includes(data.type)) {
const { addNode, start } = getStoreActions().network;
const lnNode = await addNode({
const newNode = await addNode({
id: activeId,
type: data.type,
version: data.version,
});
actions.addNode({ lnNode, position });
actions.addNode({ newNode, position });
actions.redrawChart();
if (network.status === Status.Started) {
await start(activeId);

34
src/store/models/network.ts

@ -3,6 +3,8 @@ import { join } from 'path';
import { push } from 'connected-react-router';
import { Action, action, Computed, computed, Thunk, thunk } from 'easy-peasy';
import {
BitcoindVersion,
BitcoinNode,
CLightningVersion,
CommonNode,
LightningNode,
@ -13,6 +15,7 @@ import { Network, StoreInjections } from 'types';
import { initChartFromNetwork } from 'utils/chart';
import { rm } from 'utils/files';
import {
createBitcoindNetworkNode,
createCLightningNetworkNode,
createLndNetworkNode,
createNetwork,
@ -49,10 +52,14 @@ export interface NetworkModel {
>;
addNode: Thunk<
NetworkModel,
{ id: number; type: string; version: LndVersion | CLightningVersion },
{
id: number;
type: string;
version: LndVersion | CLightningVersion | BitcoindVersion;
},
StoreInjections,
RootModel,
Promise<LightningNode>
Promise<LightningNode | BitcoinNode>
>;
removeNode: Thunk<NetworkModel, { node: LightningNode }, StoreInjections, RootModel>;
setStatus: Action<
@ -139,11 +146,23 @@ const networkModel: NetworkModel = {
const networks = getState().networks;
const network = networks.find(n => n.id === id);
if (!network) throw new Error(l('networkByIdErr', { networkId: id }));
const node =
type === 'c-lightning'
? createCLightningNetworkNode(network, version as CLightningVersion)
: createLndNetworkNode(network, version as LndVersion);
network.nodes.lightning.push(node);
let node: LightningNode | BitcoinNode;
switch (type) {
case 'lnd':
node = createLndNetworkNode(network, version as LndVersion);
network.nodes.lightning.push(node);
break;
case 'c-lightning':
node = createCLightningNetworkNode(network, version as CLightningVersion);
network.nodes.lightning.push(node);
break;
case 'bitcoind':
node = createBitcoindNetworkNode(network, version as BitcoindVersion);
network.nodes.bitcoin.push(node);
break;
default:
throw new Error(`Cannot add uknown node type '${type}' to the network`);
}
actions.setNetworks([...networks]);
await actions.save();
await injections.dockerService.saveComposeFile(network);
@ -288,6 +307,7 @@ const networkModel: NetworkModel = {
actions.setNetworks(newNetworks);
getStoreActions().designer.removeChart(networkId);
network.nodes.lightning.forEach(n => getStoreActions().lightning.removeNode(n.name));
network.nodes.bitcoin.forEach(n => getStoreActions().bitcoind.removeNode(n.name));
await actions.save();
await getStoreActions().app.clearAppCache();
}),

11
src/utils/chart.ts

@ -83,13 +83,14 @@ export const createBitcoinChartNode = (btc: BitcoinNode) => {
};
let link: ILink | undefined;
const peer = btc.peers[btc.peers.length - 1];
if (peer && btc.name < peer) {
// the first peer is always the prev node unless this is the first node in the network
const peer = btc.peers[0];
if (peer && btc.name > peer) {
// only add one link from left to right (ex: 'backend2' < 'backend3')
link = {
id: `${btc.name}-${peer}`,
from: { nodeId: btc.name, portId: 'peer-right' },
to: { nodeId: peer, portId: 'peer-left' },
id: `${peer}-${btc.name}`,
from: { nodeId: peer, portId: 'peer-right' },
to: { nodeId: btc.name, portId: 'peer-left' },
properties: {
type: 'btcpeer',
},

24
src/utils/network.ts

@ -2,6 +2,7 @@ import { debug } from 'electron-log';
import { join } from 'path';
import detectPort from 'detect-port';
import {
BitcoindVersion,
BitcoinNode,
CLightningNode,
CLightningVersion,
@ -106,22 +107,32 @@ export const createCLightningNetworkNode = (
export const createBitcoindNetworkNode = (
network: Network,
status: Status,
version: BitcoindVersion,
status = Status.Stopped,
): BitcoinNode => {
const { bitcoin } = network.nodes;
const id = bitcoin.length ? Math.max(...bitcoin.map(n => n.id)) + 1 : 0;
const name = `backend${id + 1}`;
return {
const node: BitcoinNode = {
id,
networkId: network.id,
name: name,
type: 'bitcoin',
implementation: 'bitcoind',
version: '0.18.1',
version,
peers: [],
status,
ports: { rpc: BasePorts.bitcoind.rest + id },
};
// peer up with the previous node in both directions
if (bitcoin.length > 0) {
const prev = bitcoin[bitcoin.length - 1];
node.peers.push(prev.name);
prev.peers.push(node.name);
}
return node;
};
export const createNetwork = (config: {
@ -150,12 +161,7 @@ export const createNetwork = (config: {
const { bitcoin, lightning } = network.nodes;
range(bitcoindNodes).forEach(() => {
bitcoin.push(createBitcoindNetworkNode(network, status));
});
// peer with the nodes immediately before and after
bitcoin.forEach((n, i) => {
if (i > 0) n.peers.push(bitcoin[i - 1].name);
if (i < bitcoin.length - 1) n.peers.push(bitcoin[i + 1].name);
bitcoin.push(createBitcoindNetworkNode(network, BitcoindVersion.latest, status));
});
// add lightning nodes in an alternating pattern

Loading…
Cancel
Save