diff --git a/src/components/common/RenameNodeModal.spec.tsx b/src/components/common/RenameNodeModal.spec.tsx
index c77a128005..5a8f14739b 100644
--- a/src/components/common/RenameNodeModal.spec.tsx
+++ b/src/components/common/RenameNodeModal.spec.tsx
@@ -6,9 +6,9 @@ import { initChartFromNetwork } from 'utils/chart';
import { defaultRepoState } from 'utils/constants';
import { createNetwork } from 'utils/network';
import {
+ bitcoinServiceMock,
injections,
lightningServiceMock,
- bitcoinServiceMock,
litdServiceMock,
renderWithProviders,
tapServiceMock,
@@ -41,6 +41,7 @@ describe('RenameNodeModal', () => {
repoState: defaultRepoState,
managedImages: testManagedImages,
customImages: [],
+ manualMineCount: 6,
});
const initialState = {
network: {
diff --git a/src/components/designer/bitcoin/actions/MineBlocksInput.tsx b/src/components/designer/bitcoin/actions/MineBlocksInput.tsx
index c8bb21e7f8..5577a99f5f 100644
--- a/src/components/designer/bitcoin/actions/MineBlocksInput.tsx
+++ b/src/components/designer/bitcoin/actions/MineBlocksInput.tsx
@@ -1,11 +1,11 @@
-import React, { useState } from 'react';
+import React from 'react';
import { useAsyncCallback } from 'react-async-hook';
import { ToolOutlined } from '@ant-design/icons';
import styled from '@emotion/styled';
import { Button, Form, Input, InputNumber } from 'antd';
import { usePrefixedTranslation } from 'hooks';
import { BitcoinNode } from 'shared/types';
-import { useStoreActions } from 'store';
+import { useStoreActions, useStoreState } from 'store';
const InputGroup = Input.Group;
@@ -20,24 +20,32 @@ const Styled = {
const MineBlocksInput: React.FC<{ node: BitcoinNode }> = ({ node }) => {
const { l } = usePrefixedTranslation('cmps.designer.bitcoind.MineBlocksInput');
- const [value, setValue] = useState(6);
+ const network = useStoreState(s => s.network.networkById(node.networkId));
const { notify } = useStoreActions(s => s.app);
const { mine } = useStoreActions(s => s.bitcoin);
+ const updateManualMineCount = useStoreActions(
+ actions => actions.network.updateManualMineCount,
+ );
+
const mineAsync = useAsyncCallback(async () => {
try {
- await mine({ blocks: value, node });
+ await mine({ blocks: network.manualMineCount, node });
} catch (error: any) {
notify({ message: l('error'), error });
}
});
+ const handleManualMineCountChange = (count: number) => {
+ updateManualMineCount({ id: node.networkId, count });
+ };
+
return (
setValue(parseInt(v as any))}
+ onChange={v => handleManualMineCountChange(parseInt(v as any))}
disabled={mineAsync.loading}
/>
{
repoState: defaultRepoState,
managedImages: testManagedImages,
customImages: [],
+ manualMineCount: 6,
});
const initialState = {
diff --git a/src/components/designer/lightning/actions/CreateInvoiceModal.spec.tsx b/src/components/designer/lightning/actions/CreateInvoiceModal.spec.tsx
index c092bedda4..7a1939ee6b 100644
--- a/src/components/designer/lightning/actions/CreateInvoiceModal.spec.tsx
+++ b/src/components/designer/lightning/actions/CreateInvoiceModal.spec.tsx
@@ -150,6 +150,7 @@ describe('CreateInvoiceModal', () => {
repoState: defaultRepoState,
managedImages: testManagedImages,
customImages: [],
+ manualMineCount: 6,
});
const asset: LightningNodeChannelAsset = {
id: 'abcd',
diff --git a/src/components/designer/lightning/actions/OpenChannelModal.spec.tsx b/src/components/designer/lightning/actions/OpenChannelModal.spec.tsx
index 8e1a4ac755..d76da8723d 100644
--- a/src/components/designer/lightning/actions/OpenChannelModal.spec.tsx
+++ b/src/components/designer/lightning/actions/OpenChannelModal.spec.tsx
@@ -288,6 +288,7 @@ describe('OpenChannelModal', () => {
repoState: defaultRepoState,
managedImages: testManagedImages,
customImages: [],
+ manualMineCount: 6,
});
const asset: LightningNodeChannelAsset = {
id: 'abcd',
diff --git a/src/components/designer/lightning/actions/PayInvoiceModal.spec.tsx b/src/components/designer/lightning/actions/PayInvoiceModal.spec.tsx
index b9bf825f55..7d3c97c79f 100644
--- a/src/components/designer/lightning/actions/PayInvoiceModal.spec.tsx
+++ b/src/components/designer/lightning/actions/PayInvoiceModal.spec.tsx
@@ -149,6 +149,7 @@ describe('PayInvoiceModal', () => {
repoState: defaultRepoState,
managedImages: testManagedImages,
customImages: [],
+ manualMineCount: 6,
});
const asset: LightningNodeChannelAsset = {
id: 'abcd',
diff --git a/src/components/network/ImportNetwork.spec.tsx b/src/components/network/ImportNetwork.spec.tsx
index ded5bbb11d..9a7bf054fa 100644
--- a/src/components/network/ImportNetwork.spec.tsx
+++ b/src/components/network/ImportNetwork.spec.tsx
@@ -72,6 +72,7 @@ describe('ImportNetwork component', () => {
repoState: defaultRepoState,
managedImages: testManagedImages,
customImages: [],
+ manualMineCount: 6,
});
chart = initChartFromNetwork(network);
filesMock.read.mockResolvedValue(JSON.stringify({ network, chart }));
diff --git a/src/lib/docker/composeFile.spec.ts b/src/lib/docker/composeFile.spec.ts
index d9bfb59f46..c9605ae0e9 100644
--- a/src/lib/docker/composeFile.spec.ts
+++ b/src/lib/docker/composeFile.spec.ts
@@ -19,6 +19,7 @@ describe('ComposeFile', () => {
repoState: defaultRepoState,
managedImages: testManagedImages,
customImages: [],
+ manualMineCount: 6,
});
const btcNode = network.nodes.bitcoin[0];
const lndNode = network.nodes.lightning[0] as LndNode;
diff --git a/src/lib/docker/dockerService.spec.ts b/src/lib/docker/dockerService.spec.ts
index ace1c4520d..44b7e49ff7 100644
--- a/src/lib/docker/dockerService.spec.ts
+++ b/src/lib/docker/dockerService.spec.ts
@@ -192,6 +192,7 @@ describe('DockerService', () => {
repoState: defaultRepoState,
managedImages: testManagedImages,
customImages: [],
+ manualMineCount: 6,
});
net.nodes.lightning[0].backendName = 'invalid';
dockerService.saveComposeFile(net);
@@ -217,6 +218,7 @@ describe('DockerService', () => {
repoState: defaultRepoState,
managedImages: testManagedImages,
customImages: [],
+ manualMineCount: 6,
});
net.nodes.lightning[0].backendName = 'invalid';
dockerService.saveComposeFile(net);
@@ -242,6 +244,7 @@ describe('DockerService', () => {
repoState: defaultRepoState,
managedImages: testManagedImages,
customImages: [],
+ manualMineCount: 6,
});
net.nodes.lightning[0].backendName = 'invalid';
dockerService.saveComposeFile(net);
@@ -308,6 +311,7 @@ describe('DockerService', () => {
repoState: defaultRepoState,
managedImages: testManagedImages,
customImages: [],
+ manualMineCount: 6,
});
dockerService.saveComposeFile(net);
const { backendName } = net.nodes.lightning[0] as LitdNode;
@@ -331,6 +335,7 @@ describe('DockerService', () => {
repoState: defaultRepoState,
managedImages: testManagedImages,
customImages: [],
+ manualMineCount: 6,
});
const litdNode = net.nodes.lightning[0] as LitdNode;
litdNode.backendName = 'invalid';
@@ -357,6 +362,7 @@ describe('DockerService', () => {
repoState: defaultRepoState,
managedImages: testManagedImages,
customImages: [],
+ manualMineCount: 6,
});
net.nodes.lightning[0].implementation = 'unknown' as any;
dockerService.saveComposeFile(net);
@@ -392,6 +398,7 @@ describe('DockerService', () => {
repoState: defaultRepoState,
managedImages: testManagedImages,
customImages: [],
+ manualMineCount: 6,
});
const chart = initChartFromNetwork(net);
// return 'any' to suppress "The operand of a 'delete' operator must be optional.ts(2790)" error
@@ -522,6 +529,7 @@ describe('DockerService', () => {
repoState: defaultRepoState,
managedImages: testManagedImages,
customImages: [],
+ manualMineCount: 6,
});
const chart = initChartFromNetwork(net);
const fileData: NetworksFile = {
@@ -716,6 +724,7 @@ describe('DockerService', () => {
repoState: defaultRepoState,
managedImages: testManagedImages,
customImages: [],
+ manualMineCount: 6,
});
composeMock.upAll.mockResolvedValue(mockResult);
await dockerService.start(net);
diff --git a/src/lib/litd/litdProxyClient.spec.ts b/src/lib/litd/litdProxyClient.spec.ts
index 608b2188c4..ef8d619572 100644
--- a/src/lib/litd/litdProxyClient.spec.ts
+++ b/src/lib/litd/litdProxyClient.spec.ts
@@ -20,6 +20,7 @@ describe('LitdProxyClient', () => {
repoState: defaultRepoState,
managedImages: testManagedImages,
customImages: [],
+ manualMineCount: 6,
});
const node = network.nodes.lightning[0] as LitdNode;
let actualIpc: IpcSender;
diff --git a/src/lib/litd/litdService.spec.ts b/src/lib/litd/litdService.spec.ts
index 3760134547..883a886bb7 100644
--- a/src/lib/litd/litdService.spec.ts
+++ b/src/lib/litd/litdService.spec.ts
@@ -24,6 +24,7 @@ describe('LitdService', () => {
repoState: defaultRepoState,
managedImages: testManagedImages,
customImages: [],
+ manualMineCount: 6,
});
const node = network.nodes.lightning[0] as LitdNode;
diff --git a/src/store/models/designer.spec.ts b/src/store/models/designer.spec.ts
index 7d42244824..7139b18805 100644
--- a/src/store/models/designer.spec.ts
+++ b/src/store/models/designer.spec.ts
@@ -133,6 +133,7 @@ describe('Designer model', () => {
tapdNodes: 0,
litdNodes: 0,
customNodes: {},
+ manualMineCount: 6,
});
store.getActions().designer.setActiveId(firstNetwork().id);
const { removeChart } = store.getActions().designer;
@@ -581,6 +582,7 @@ describe('Designer model', () => {
tapdNodes: 0,
litdNodes: 0,
customNodes: {},
+ manualMineCount: 6,
});
const newId = store.getState().network.networks[1].id;
setActiveId(newId);
@@ -610,6 +612,7 @@ describe('Designer model', () => {
tapdNodes: 0,
litdNodes: 0,
customNodes: {},
+ manualMineCount: 6,
});
const newId = store.getState().network.networks[1].id;
setActiveId(newId);
@@ -647,6 +650,7 @@ describe('Designer model', () => {
tapdNodes: 0,
litdNodes: 0,
customNodes: {},
+ manualMineCount: 0,
});
const newId = store.getState().network.networks[1].id;
setActiveId(newId);
diff --git a/src/store/models/lit.spec.ts b/src/store/models/lit.spec.ts
index d808ba928c..10f7822de3 100644
--- a/src/store/models/lit.spec.ts
+++ b/src/store/models/lit.spec.ts
@@ -44,6 +44,7 @@ describe('LIT Model', () => {
repoState: defaultRepoState,
managedImages: testManagedImages,
customImages: [],
+ manualMineCount: 6,
});
const initialState = {
network: {
diff --git a/src/store/models/network.spec.ts b/src/store/models/network.spec.ts
index 745d9a2276..22df734aae 100644
--- a/src/store/models/network.spec.ts
+++ b/src/store/models/network.spec.ts
@@ -65,6 +65,7 @@ describe('Network model', () => {
tapdNodes: 0,
litdNodes: 1,
customNodes: {},
+ manualMineCount: 6,
};
beforeEach(() => {
@@ -1023,6 +1024,55 @@ describe('Network model', () => {
});
});
+ describe('ManualMineCount', () => {
+ beforeEach(async () => {
+ await store.getActions().network.addNetwork(addNetworkArgs);
+ });
+
+ it('should set manual mine count for a network', () => {
+ const { setManualMineCount } = store.getActions().network;
+ const networkId = firstNetwork().id;
+
+ expect(firstNetwork().manualMineCount).toBe(6);
+ setManualMineCount({ id: networkId, count: 10 });
+ expect(firstNetwork().manualMineCount).toBe(10);
+ });
+
+ it('should fail to set manual mine count with invalid network id', () => {
+ const { setManualMineCount } = store.getActions().network;
+
+ expect(() => setManualMineCount({ id: 999, count: 10 })).toThrow(
+ "Network with the id '999' was not found.",
+ );
+ });
+
+ it('should update manual mine count and persist changes', async () => {
+ const { updateManualMineCount } = store.getActions().network;
+ const networkId = firstNetwork().id;
+
+ (injections.dockerService.saveComposeFile as jest.Mock).mockClear();
+ (injections.dockerService.saveNetworks as jest.Mock).mockClear();
+
+ await updateManualMineCount({ id: networkId, count: 15 });
+
+ expect(firstNetwork().manualMineCount).toBe(15);
+
+ expect(injections.dockerService.saveComposeFile).toHaveBeenCalledTimes(1);
+ expect(injections.dockerService.saveNetworks).toHaveBeenCalledTimes(1);
+ });
+
+ it('should fail to update manual mine count with invalid network id', () => {
+ const { updateManualMineCount } = store.getActions().network;
+
+ (injections.dockerService.saveComposeFile as jest.Mock).mockClear();
+ (injections.dockerService.saveNetworks as jest.Mock).mockClear();
+
+ expect(() => updateManualMineCount({ id: 999, count: 10 })).rejects.toThrow(
+ "Network with the id '999' was not found.",
+ );
+ });
+ });
+
describe('Other actions', () => {
it('should remove a network', async () => {
expect(store.getState().network.networks).toHaveLength(0);
diff --git a/src/store/models/network.ts b/src/store/models/network.ts
index 46093e7a1a..c52633c57b 100644
--- a/src/store/models/network.ts
+++ b/src/store/models/network.ts
@@ -51,6 +51,7 @@ interface AddNetworkArgs {
tapdNodes: number;
litdNodes: number;
customNodes: Record;
+ manualMineCount: number;
}
export interface AutoMinerModel {
@@ -192,6 +193,14 @@ export interface NetworkModel {
setAutoMineMode: Action;
setMiningState: Action;
mineBlock: Thunk;
+ setManualMineCount: Action;
+ updateManualMineCount: Thunk<
+ NetworkModel,
+ { id: number; count: number },
+ StoreInjections,
+ RootModel,
+ Promise
+ >;
}
const networkModel: NetworkModel = {
@@ -286,6 +295,7 @@ const networkModel: NetworkModel = {
managedImages: computedManagedImages,
customImages,
basePorts: settings.basePorts,
+ manualMineCount: 6,
});
actions.add(network);
const { networks } = getState();
@@ -1074,6 +1084,25 @@ const networkModel: NetworkModel = {
}
},
),
+ setManualMineCount: action((state, { id, count }) => {
+ const network = state.networks.find(n => n.id === id);
+ if (!network) throw new Error(l('networkByIdErr', { networkId: id }));
+ network.manualMineCount = count;
+ }),
+ updateManualMineCount: thunk(
+ async (actions, { id, count }, { getState, injections }) => {
+ const networks = getState().networks;
+ const network = networks.find(n => n.id === id);
+ if (!network) throw new Error(l('networkByIdErr', { networkId: id }));
+
+ // Update the manual mine count
+ actions.setManualMineCount({ id, count });
+
+ // Save the changes to disk
+ await actions.save();
+ await injections.dockerService.saveComposeFile(network);
+ },
+ ),
};
export default networkModel;
diff --git a/src/types/index.ts b/src/types/index.ts
index a70c89830b..ee01a5d322 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -31,6 +31,7 @@ export interface Network {
lightning: LightningNode[];
tap: TapNode[];
};
+ manualMineCount: number;
}
/**
diff --git a/src/utils/network.spec.ts b/src/utils/network.spec.ts
index 05efdf07dd..c7024963a1 100644
--- a/src/utils/network.spec.ts
+++ b/src/utils/network.spec.ts
@@ -163,6 +163,7 @@ describe('Network Utils', () => {
repoState: defaultRepoState,
managedImages: testManagedImages,
customImages: [],
+ manualMineCount: 6,
});
});
@@ -507,6 +508,7 @@ describe('Network Utils', () => {
repoState: defaultRepoState,
managedImages: testManagedImages,
customImages: [],
+ manualMineCount: 6,
});
});
diff --git a/src/utils/network.ts b/src/utils/network.ts
index d8106052f1..5f826c7b5f 100644
--- a/src/utils/network.ts
+++ b/src/utils/network.ts
@@ -456,6 +456,7 @@ export const createNetwork = (config: {
customImages: { image: CustomImage; count: number }[];
status?: Status;
basePorts?: NodeBasePorts;
+ manualMineCount: number;
}): Network => {
const {
id,
@@ -471,6 +472,7 @@ export const createNetwork = (config: {
managedImages,
customImages,
basePorts,
+ manualMineCount,
} = config;
// need explicit undefined check because Status.Starting is 0
const status = config.status !== undefined ? config.status : Status.Stopped;
@@ -487,6 +489,7 @@ export const createNetwork = (config: {
tap: [],
},
autoMineMode: AutoMineMode.AutoOff,
+ manualMineCount,
};
const { bitcoin, lightning } = network.nodes;
diff --git a/src/utils/tests/helpers.ts b/src/utils/tests/helpers.ts
index 456214d71c..537e1cbc1f 100644
--- a/src/utils/tests/helpers.ts
+++ b/src/utils/tests/helpers.ts
@@ -240,6 +240,7 @@ export const getNetwork = (
repoState: defaultRepoState,
managedImages: testManagedImages,
customImages: [],
+ manualMineCount: 6,
};
if (tapNodeCount > 0) {
config.lndNodes = 0;