Skip to content

Commit

Permalink
feat: use simln docker image for node activity simulation
Browse files Browse the repository at this point in the history
  • Loading branch information
Extheoisah committed Apr 3, 2024
1 parent ad28499 commit 318d828
Show file tree
Hide file tree
Showing 13 changed files with 345 additions and 30 deletions.
4 changes: 2 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
}
],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
"source.fixAll.eslint": "explicit"
},
"typescript.tsdk": "node_modules/typescript/lib"
"typescript.tsdk": "node_modules/typescript/lib",
}
4 changes: 4 additions & 0 deletions docker/nodes.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@
"compatibility": {
"0.3.0-alpha": "0.16.0-beta"
}
},
"simln": {
"latest": "0.2.0",
"versions": ["0.2.0"]
}
}
}
16 changes: 8 additions & 8 deletions src/components/designer/ActivityGenerator.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React, { useState } from 'react';
import React from 'react';
import styled from '@emotion/styled';
import { Alert, Button, Col, Form, InputNumber, Row, Select, Slider } from 'antd';
import { usePrefixedTranslation } from 'hooks';
import { CLightningNode, LightningNode, LndNode } from 'shared/types';
import { useStoreActions, useStoreState } from 'store';
import { ActivityInfo, Network, SimulationActivityNode } from 'types';
import { AddActivityInvalidState } from './default/cards/ActivityDesignerCard';

const Styled = {
ActivityGen: styled.div`
Expand Down Expand Up @@ -89,28 +90,26 @@ interface Props {
activities: any;
activityInfo: ActivityInfo;
network: Network;
addActivityInvalidState: AddActivityInvalidState | null;
setAddActivityInvalidState: (state: AddActivityInvalidState | null) => void;
toggle: () => void;
updater: AvtivityUpdater;
reset: () => void;
}
interface AddActivityInvalidState {
state: 'warning' | 'error';
message: string;
}

const ActivityGenerator: React.FC<Props> = ({
visible,
network,
activityInfo,
addActivityInvalidState,
setAddActivityInvalidState,
toggle,
reset,
updater,
}) => {
if (!visible) return null;

const editActivityId = activityInfo.id;
const [addActivityInvalidState, setAddActivityInvalidState] =
useState<AddActivityInvalidState | null>(null);
const { sourceNode, targetNode, frequency, amount } = activityInfo;

const { l } = usePrefixedTranslation('cmps.designer.ActivityGenerator');
Expand Down Expand Up @@ -170,6 +169,7 @@ const ActivityGenerator: React.FC<Props> = ({
setAddActivityInvalidState({
state: 'error',
message: '',
action: 'save',
});
return;
}
Expand Down Expand Up @@ -290,7 +290,7 @@ const ActivityGenerator: React.FC<Props> = ({
</Styled.Save>
</Styled.NodeWrapper>
</Styled.ActivityForm>
{addActivityInvalidState?.state && (
{addActivityInvalidState?.state && addActivityInvalidState.action === 'save' && (
<Alert
key={addActivityInvalidState.state}
onClose={() => setAddActivityInvalidState(null)}
Expand Down
123 changes: 110 additions & 13 deletions src/components/designer/default/cards/ActivityDesignerCard.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
import {
ArrowDownOutlined,
ArrowRightOutlined,
ArrowUpOutlined,
DeleteOutlined,
CopyOutlined,
DeleteOutlined,
} from '@ant-design/icons';
import styled from '@emotion/styled';
import { Button, Tooltip } from 'antd';
import { Alert, Button, Tooltip } from 'antd';
import { usePrefixedTranslation } from 'hooks';
import { useTheme } from 'hooks/useTheme';
import { Status } from 'shared/types';
import { getDocker } from 'lib/docker/dockerService';
import { useStoreActions } from 'store';
import { ThemeColors } from 'theme/colors';
import { ActivityInfo, Network, SimulationActivity } from 'types';
import ActivityGenerator from '../../ActivityGenerator';
import { useStoreActions } from 'store';

const Styled = {
AddNodes: styled.div`
Expand Down Expand Up @@ -135,6 +137,12 @@ interface Props {
visible: boolean;
}

export interface AddActivityInvalidState {
state: 'warning' | 'error';
action: 'start' | 'save';
message: string;
}

const defaultActivityInfo: ActivityInfo = {
id: undefined,
sourceNode: undefined,
Expand All @@ -146,18 +154,94 @@ const defaultActivityInfo: ActivityInfo = {
const ActivityDesignerCard: React.FC<Props> = ({ visible, network }) => {
const [isSimulationActive, setIsStartSimulationActive] = React.useState(false);
const [isAddActivityActive, setIsAddActivityActive] = React.useState(false);

const { addSimulationActivity } = useStoreActions(s => s.network);
const { lightning } = network.nodes;

const [addActivityInvalidState, setAddActivityInvalidState] =
useState<AddActivityInvalidState | null>(null);
const [activityInfo, setActivityInfo] = useState<ActivityInfo>(defaultActivityInfo);

const theme = useTheme();
const { l } = usePrefixedTranslation(
'cmps.designer.default.cards.ActivityDesignerCard',
);
const numberOfActivities = network.simulationActivities.length;
const { removeSimulationActivity } = useStoreActions(s => s.network);
const {
addSimulationActivity,
removeSimulationActivity,
startSimulation,
stopSimulation,
} = useStoreActions(s => s.network);
const { lightning } = network.nodes;

const activities = network.simulationActivities ?? [];
const numberOfActivities = activities.length;

const isSimulationContainerRunning = async () => {
const docker = await getDocker();
const containers = await docker.listContainers();
const simContainer = containers.find(c => {
// remove the leading '/' from the container name
const name = c.Names[0].substring(1);
return name === `polar-n${network.id}-simln`;
});
return simContainer?.State === 'restarting' || simContainer?.State === 'running';
};

useEffect(() => {
isSimulationContainerRunning().then(isRunning => {
setIsStartSimulationActive(isRunning);
});
}, []);

const startSimulationActivity = () => {
if (network.status !== Status.Started) {
setAddActivityInvalidState({
state: 'warning',
message: l('startWarning'),
action: 'start',
});
setIsStartSimulationActive(false);
return;
}
if (numberOfActivities === 0) {
setIsAddActivityActive(true);
setAddActivityInvalidState({
state: 'warning',
message: l('NoActivityAddedWarning'),
action: 'start',
});
setIsStartSimulationActive(false);
return;
}
const allNotStartedNodesSet = new Set();
const nodes = network.simulationActivities.flatMap(activity => {
const activityNodes = new Set([activity.source.label, activity.destination.label]);
return lightning
.filter(node => node.status !== Status.Started && activityNodes.has(node.name))
.filter(node => {
const notStarted = !allNotStartedNodesSet.has(node.name);
if (notStarted) {
allNotStartedNodesSet.add(node.name);
}
return notStarted;
});
});
if (nodes.length > 0) {
setIsAddActivityActive(true);
setAddActivityInvalidState({
state: 'warning',
message: l('startWarning'),
action: 'start',
});
setIsStartSimulationActive(false);
return;
}
setAddActivityInvalidState(null);
if (isSimulationActive) {
setIsStartSimulationActive(false);
stopSimulation({ id: network.id });
return;
}
setIsStartSimulationActive(true);
startSimulation({ id: network.id });
};

const toggleAddActivity = () => {
setIsAddActivityActive(prev => !prev);
Expand Down Expand Up @@ -229,9 +313,11 @@ const ActivityDesignerCard: React.FC<Props> = ({ visible, network }) => {
<Styled.AddDesc>{l('addActivitiesDesc')}</Styled.AddDesc>
<ActivityGenerator
visible={isAddActivityActive}
activities={network.simulationActivities}
activities={activities}
activityInfo={activityInfo}
network={network}
addActivityInvalidState={addActivityInvalidState}
setAddActivityInvalidState={setAddActivityInvalidState}
toggle={toggleAddActivity}
updater={resolveUpdater}
reset={reset}
Expand All @@ -242,7 +328,7 @@ const ActivityDesignerCard: React.FC<Props> = ({ visible, network }) => {
{` (${numberOfActivities})`}
</p>
<Styled.ActivityButtons>
{network.simulationActivities.map(activity => (
{activities.map(activity => (
<Styled.Activity
key={`id-${activity.id}-${activity.source.address}-${activity.destination.address}`}
colors={theme.dragNode}
Expand All @@ -266,10 +352,21 @@ const ActivityDesignerCard: React.FC<Props> = ({ visible, network }) => {
</Styled.Activity>
))}
</Styled.ActivityButtons>
{addActivityInvalidState?.state && addActivityInvalidState.action === 'start' && (
<Alert
key={addActivityInvalidState.state}
onClose={() => setAddActivityInvalidState(null)}
type="warning"
message={addActivityInvalidState?.message || l('startWarning')}
closable={true}
showIcon
style={{ marginTop: 20 }}
/>
)}
<Styled.StartStopButton
colors={theme.dragNode}
active={isSimulationActive}
onClick={() => setIsStartSimulationActive(!isSimulationActive)}
onClick={startSimulationActivity}
>
{isSimulationActive ? l('stop') : l('start')}
</Styled.StartStopButton>
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 @@ -102,6 +102,8 @@
"cmps.designer.default.cards.ActivityDesignerCard.duplicateBtnTip": "Duplicate",
"cmps.designer.default.cards.ActivityDesignerCard.start": "Start",
"cmps.designer.default.cards.ActivityDesignerCard.stop": "Stop",
"cmps.designer.default.cards.ActivityDesignerCard.startWarning": "The network and activity nodes must be started to run simulations",
"cmps.designer.default.cards.ActivityDesignerCard.NoActivityAddedWarning": "Please add at least one activity to run simulations",
"cmps.designer.default.cards.NetworkDesignerCard.showVersions": "Show All Versions",
"cmps.designer.default.cards.NetworkDesignerCard.checkUpdates": "Check for new Node Versions",
"cmps.designer.default.ImageUpdatesModal.title": "Check for new Node Versions",
Expand Down
14 changes: 13 additions & 1 deletion src/lib/docker/composeFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
} from 'shared/types';
import { bitcoinCredentials, dockerConfigs, eclairCredentials } from 'utils/constants';
import { getContainerName } from 'utils/network';
import { bitcoind, clightning, eclair, lnd, tapd } from './nodeTemplates';
import { bitcoind, clightning, eclair, lnd, simln, tapd } from './nodeTemplates';

export interface ComposeService {
image: string;
Expand Down Expand Up @@ -47,12 +47,24 @@ class ComposeFile {
environment: {
USERID: '${USERID:-1000}',
GROUPID: '${GROUPID:-1000}',
...service.environment,
},
stop_grace_period: '2m',
...service,
};
}

addSimLn(networkId: number) {
const svc: ComposeService = simln(
dockerConfigs['simln'].name,
`polar-n${networkId}-simln`,
dockerConfigs['simln'].imageName,
dockerConfigs['simln'].command,
{ ...dockerConfigs['simln'].env },
);
this.addService(svc);
}

addBitcoind(node: BitcoinNode) {
const { name, version, ports } = node;
const { rpc, p2p, zmqBlock, zmqTx } = ports;
Expand Down
Loading

0 comments on commit 318d828

Please sign in to comment.