@constellation-network/node-pilot 0.14.0-testnet → 0.16.0-intnet
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -11
- package/dist/checks/check-disk-space.d.ts +5 -0
- package/dist/checks/check-disk-space.js +78 -0
- package/dist/checks/check-initial-setup.js +2 -1
- package/dist/checks/check-network.js +1 -0
- package/dist/checks/check-pilot.js +9 -0
- package/dist/checks/check-project.js +6 -1
- package/dist/commands/config.js +9 -4
- package/dist/commands/test.js +5 -2
- package/dist/helpers/project-helper.js +1 -1
- package/dist/helpers/status-table-helper.js +2 -2
- package/dist/services/cluster-service.d.ts +4 -1
- package/dist/services/cluster-service.js +32 -6
- package/dist/services/delegated-staking-service.d.ts +26 -0
- package/dist/services/delegated-staking-service.js +65 -0
- package/dist/services/migration-service.js +4 -0
- package/dist/services/systemd-service.js +2 -2
- package/oclif.manifest.json +1 -1
- package/package.json +2 -2
- package/projects/hypergraph/Dockerfile +1 -1
- package/projects/hypergraph/scripts/install-dependencies.sh +2 -2
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ $ npm install -g @constellation-network/node-pilot
|
|
|
21
21
|
$ cpilot COMMAND
|
|
22
22
|
running command...
|
|
23
23
|
$ cpilot (--version|-v)
|
|
24
|
-
@constellation-network/node-pilot/0.
|
|
24
|
+
@constellation-network/node-pilot/0.16.0-intnet darwin-arm64 node-v22.15.0
|
|
25
25
|
$ cpilot --help [COMMAND]
|
|
26
26
|
USAGE
|
|
27
27
|
$ cpilot COMMAND
|
|
@@ -72,7 +72,7 @@ EXAMPLES
|
|
|
72
72
|
$ cpilot clean
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
_See code: [src/commands/clean.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.
|
|
75
|
+
_See code: [src/commands/clean.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.16.0-intnet/src/commands/clean.ts)_
|
|
76
76
|
|
|
77
77
|
## `cpilot config`
|
|
78
78
|
|
|
@@ -89,7 +89,7 @@ EXAMPLES
|
|
|
89
89
|
$ cpilot config
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
_See code: [src/commands/config.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.
|
|
92
|
+
_See code: [src/commands/config.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.16.0-intnet/src/commands/config.ts)_
|
|
93
93
|
|
|
94
94
|
## `cpilot config get [NAME]`
|
|
95
95
|
|
|
@@ -113,7 +113,7 @@ EXAMPLES
|
|
|
113
113
|
$ cpilot config get gl0:CL_PUBLIC_HTTP_PORT
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
-
_See code: [src/commands/config/get.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.
|
|
116
|
+
_See code: [src/commands/config/get.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.16.0-intnet/src/commands/config/get.ts)_
|
|
117
117
|
|
|
118
118
|
## `cpilot config set NAME VALUE`
|
|
119
119
|
|
|
@@ -136,7 +136,7 @@ EXAMPLES
|
|
|
136
136
|
$ cpilot config set gl0:CL_PUBLIC_HTTP_PORT 9000
|
|
137
137
|
```
|
|
138
138
|
|
|
139
|
-
_See code: [src/commands/config/set.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.
|
|
139
|
+
_See code: [src/commands/config/set.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.16.0-intnet/src/commands/config/set.ts)_
|
|
140
140
|
|
|
141
141
|
## `cpilot help [COMMAND]`
|
|
142
142
|
|
|
@@ -173,7 +173,7 @@ EXAMPLES
|
|
|
173
173
|
$ cpilot info
|
|
174
174
|
```
|
|
175
175
|
|
|
176
|
-
_See code: [src/commands/info.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.
|
|
176
|
+
_See code: [src/commands/info.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.16.0-intnet/src/commands/info.ts)_
|
|
177
177
|
|
|
178
178
|
## `cpilot logs LAYER`
|
|
179
179
|
|
|
@@ -199,7 +199,7 @@ EXAMPLES
|
|
|
199
199
|
$ cpilot logs
|
|
200
200
|
```
|
|
201
201
|
|
|
202
|
-
_See code: [src/commands/logs.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.
|
|
202
|
+
_See code: [src/commands/logs.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.16.0-intnet/src/commands/logs.ts)_
|
|
203
203
|
|
|
204
204
|
## `cpilot restart [LAYER]`
|
|
205
205
|
|
|
@@ -227,7 +227,7 @@ EXAMPLES
|
|
|
227
227
|
$ cpilot restart
|
|
228
228
|
```
|
|
229
229
|
|
|
230
|
-
_See code: [src/commands/restart.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.
|
|
230
|
+
_See code: [src/commands/restart.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.16.0-intnet/src/commands/restart.ts)_
|
|
231
231
|
|
|
232
232
|
## `cpilot shutdown`
|
|
233
233
|
|
|
@@ -244,7 +244,7 @@ EXAMPLES
|
|
|
244
244
|
$ cpilot shutdown
|
|
245
245
|
```
|
|
246
246
|
|
|
247
|
-
_See code: [src/commands/shutdown.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.
|
|
247
|
+
_See code: [src/commands/shutdown.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.16.0-intnet/src/commands/shutdown.ts)_
|
|
248
248
|
|
|
249
249
|
## `cpilot status`
|
|
250
250
|
|
|
@@ -258,7 +258,7 @@ DESCRIPTION
|
|
|
258
258
|
Display node status and configuration settings
|
|
259
259
|
```
|
|
260
260
|
|
|
261
|
-
_See code: [src/commands/status.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.
|
|
261
|
+
_See code: [src/commands/status.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.16.0-intnet/src/commands/status.ts)_
|
|
262
262
|
|
|
263
263
|
## `cpilot uninstall`
|
|
264
264
|
|
|
@@ -275,5 +275,5 @@ EXAMPLES
|
|
|
275
275
|
$ cpilot uninstall
|
|
276
276
|
```
|
|
277
277
|
|
|
278
|
-
_See code: [src/commands/uninstall.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.
|
|
278
|
+
_See code: [src/commands/uninstall.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.16.0-intnet/src/commands/uninstall.ts)_
|
|
279
279
|
<!-- commandsstop -->
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { clm } from "../clm.js";
|
|
2
|
+
import { configStore } from "../config-store.js";
|
|
3
|
+
import { shellService } from "../services/shell-service.js";
|
|
4
|
+
// Utility to parse human-readable sizes (e.g., 10M, 2.5G) to bytes
|
|
5
|
+
function parseSize(sizeStr) {
|
|
6
|
+
const multipliers = { '': 1, 'G': 1024 ** 3, 'K': 1024, 'M': 1024 ** 2, 'P': 1024 ** 5, 'T': 1024 ** 4 };
|
|
7
|
+
const match = sizeStr.match(/([\d.]+)([KMGTP]?)/i);
|
|
8
|
+
if (!match)
|
|
9
|
+
return 0;
|
|
10
|
+
const num = Number.parseFloat(match[1]);
|
|
11
|
+
const unit = match[2].toUpperCase();
|
|
12
|
+
return num * (multipliers[unit] || 1);
|
|
13
|
+
}
|
|
14
|
+
// Utility to format bytes to human-readable string
|
|
15
|
+
function formatBytes(bytes) {
|
|
16
|
+
if (bytes < 1024)
|
|
17
|
+
return `${bytes} B`;
|
|
18
|
+
const units = ['K', 'M', 'G', 'T', 'P'];
|
|
19
|
+
let i = -1;
|
|
20
|
+
do {
|
|
21
|
+
bytes /= 1024;
|
|
22
|
+
i++;
|
|
23
|
+
} while (bytes >= 1024 && i < units.length - 1);
|
|
24
|
+
return `${bytes.toFixed(2)}${units[i]}`;
|
|
25
|
+
}
|
|
26
|
+
export const checkDiskSpace = {
|
|
27
|
+
async checkDiskUsage() {
|
|
28
|
+
const logSize = await this.getReclaimableDiskSpace('logs');
|
|
29
|
+
const dataSize = await this.getReclaimableDiskSpace('data');
|
|
30
|
+
console.log(`\n${logSize} ${dataSize}`);
|
|
31
|
+
await this.getDockerReclaimableDiskUsage();
|
|
32
|
+
},
|
|
33
|
+
async getDockerReclaimableDiskUsage() {
|
|
34
|
+
// Get docker system disk usage summary
|
|
35
|
+
const output = await shellService.runCommandWithOutput('docker system df --format "{{json .}}"');
|
|
36
|
+
// docker system df --format outputs one JSON object per line
|
|
37
|
+
let totalBytes = 0;
|
|
38
|
+
const details = [];
|
|
39
|
+
for (const line of output.split('\n')) {
|
|
40
|
+
if (!line.trim())
|
|
41
|
+
continue;
|
|
42
|
+
try {
|
|
43
|
+
const obj = JSON.parse(line);
|
|
44
|
+
// Try to sum up reclaimable space from images, containers, volumes, and build cache
|
|
45
|
+
if (obj.Reclaimable && obj.Reclaimable !== "") {
|
|
46
|
+
// Reclaimable is like "1.23GB (45%)" or "123MB (10%)"
|
|
47
|
+
const match = obj.Reclaimable.match(/([\d.]+)\s*([KMGTP]?B)/i);
|
|
48
|
+
if (match) {
|
|
49
|
+
const num = Number.parseFloat(match[1]);
|
|
50
|
+
const unit = match[2].replace('B', ''); // Remove trailing B
|
|
51
|
+
const multipliers = { '': 1, 'G': 1024 ** 3, 'K': 1024, 'M': 1024 ** 2, 'P': 1024 ** 5, 'T': 1024 ** 4 };
|
|
52
|
+
totalBytes += num * (multipliers[unit.toUpperCase()] || 1);
|
|
53
|
+
details.push(`${obj.Type}: ${match[0]}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Ignore parse errors
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
clm.preStep(`Docker reclaimable disk usage: ${formatBytes(totalBytes)}${details.length > 0 ? ' (' + details.join(', ') + ')' : ''}`);
|
|
62
|
+
return formatBytes(totalBytes);
|
|
63
|
+
},
|
|
64
|
+
async getReclaimableDiskSpace(area) {
|
|
65
|
+
const { layersToRun } = configStore.getProjectInfo();
|
|
66
|
+
const logPaths = layersToRun.map(l => `${l}/${area}`).join(' ');
|
|
67
|
+
const logTable = await shellService.runProjectCommandWithOutput(`du -sh ${logPaths}`);
|
|
68
|
+
let totalBytes = 0;
|
|
69
|
+
const lines = logTable.split('\n');
|
|
70
|
+
for (const line of lines) {
|
|
71
|
+
const [size] = line.trim().split(/\s+/);
|
|
72
|
+
if (size)
|
|
73
|
+
totalBytes += parseSize(size);
|
|
74
|
+
}
|
|
75
|
+
clm.preStep(`Total reclaimable disk space in ${area}: ${formatBytes(totalBytes)}`);
|
|
76
|
+
return formatBytes(totalBytes);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
@@ -45,8 +45,9 @@ export const checkInitialSetup = {
|
|
|
45
45
|
const usersWithPilot = [];
|
|
46
46
|
for (const folder of userFolders) {
|
|
47
47
|
const dirPath = path.join(homeFolder, folder, '.node-pilot');
|
|
48
|
+
const prefix = isLinux ? 'sudo ' : '';
|
|
48
49
|
// eslint-disable-next-line no-await-in-loop
|
|
49
|
-
const exists = await shellService.runCommandWithOutput(
|
|
50
|
+
const exists = await shellService.runCommandWithOutput(`${prefix}test -d "${dirPath}" && echo 1 || echo 0`);
|
|
50
51
|
if (exists === '1')
|
|
51
52
|
usersWithPilot.push(folder);
|
|
52
53
|
}
|
|
@@ -26,6 +26,7 @@ export const checkNetwork = {
|
|
|
26
26
|
if (!isDockerRunning && found) {
|
|
27
27
|
clm.warn('Node ID already exists in the cluster.');
|
|
28
28
|
clm.warn('You need to shutdown your node from a previous installation before continuing.');
|
|
29
|
+
clm.warn('If you recently left the cluster, you may need to wait for your Node Id to be cleared. ~1 minute');
|
|
29
30
|
clm.error(`Or to change the node ID, configure the Key File: use ${chalk.cyan('cpilot config')}, and select ${chalk.cyan('Key File')}`);
|
|
30
31
|
}
|
|
31
32
|
// configStore.setProjectFlag('duplicateNodeIdChecked', true);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
1
2
|
import os from "node:os";
|
|
2
3
|
import semver from "semver";
|
|
3
4
|
import { clm } from "../clm.js";
|
|
@@ -72,6 +73,14 @@ export const checkNodePilot = {
|
|
|
72
73
|
if (hasMajorMinorChange) {
|
|
73
74
|
clm.step('Updating scripts and configuration files...');
|
|
74
75
|
projectHelper.upgradeHypergraph();
|
|
76
|
+
if (fs.existsSync('/var/run/reboot-required')) {
|
|
77
|
+
clm.warn('A system update and reboot is required.');
|
|
78
|
+
await promptHelper.doYouWishToContinue();
|
|
79
|
+
await shellService.runCommand('sudo apt-get update && sudo apt-get upgrade -y');
|
|
80
|
+
if (await promptHelper.confirmPrompt('Do you want to reboot now?')) {
|
|
81
|
+
await shellService.runCommand('sudo reboot');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
75
84
|
}
|
|
76
85
|
clm.postStep('Update completed. Run cpilot again to use the latest version');
|
|
77
86
|
process.exit(0);
|
|
@@ -16,6 +16,11 @@ function getJavaMemoryOptions(network, mem) {
|
|
|
16
16
|
// return `-Xms${mem}g -Xmx${mem}g -XX:+UnlockExperimentalVMOptions${linuxOpt} -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./heap_dumps/ -XX:+ExitOnOutOfMemoryError`;
|
|
17
17
|
return `-Xms${mem}g -Xmx${mem}g${linuxOpt} -XX:+UseStringDeduplication`;
|
|
18
18
|
}
|
|
19
|
+
if (network === 'integrationnet') {
|
|
20
|
+
const linuxOpt = (os.platform() === 'linux') ? ' -XX:+UseZGC -XX:+ZGenerational' : '';
|
|
21
|
+
// return `-Xms${mem}g -Xmx${mem}g -XX:+UnlockExperimentalVMOptions${linuxOpt} -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./heap_dumps/ -XX:+ExitOnOutOfMemoryError`;
|
|
22
|
+
return `-Xms${mem - 2}g -Xmx${mem}g${linuxOpt} -XX:+UseStringDeduplication`;
|
|
23
|
+
}
|
|
19
24
|
return `-Xms1024M -Xmx${mem}g -Xss256K`;
|
|
20
25
|
}
|
|
21
26
|
export const checkProject = {
|
|
@@ -48,7 +53,7 @@ export const checkProject = {
|
|
|
48
53
|
answer--;
|
|
49
54
|
let subLayerMem = 0;
|
|
50
55
|
let mainLayerMem = 0;
|
|
51
|
-
if (currentNetwork === 'testnet') {
|
|
56
|
+
if (currentNetwork === 'testnet' || currentNetwork === 'integrationnet') {
|
|
52
57
|
// Divide equally between layers with max of 10GB each
|
|
53
58
|
subLayerMem = layersToRun.length > 1 ? Math.floor(answer / 3) : 0;
|
|
54
59
|
subLayerMem = Math.min(subLayerMem, 5);
|
package/dist/commands/config.js
CHANGED
|
@@ -9,6 +9,7 @@ import { configHelper } from "../helpers/config-helper.js";
|
|
|
9
9
|
import { keyFileHelper } from "../helpers/key-file-helper.js";
|
|
10
10
|
import { projectHelper } from "../helpers/project-helper.js";
|
|
11
11
|
import { promptHelper } from "../helpers/prompt-helper.js";
|
|
12
|
+
import { delegatedStakingService } from "../services/delegated-staking-service.js";
|
|
12
13
|
export default class Config extends Command {
|
|
13
14
|
static description = 'Update configuration settings';
|
|
14
15
|
static examples = [
|
|
@@ -20,12 +21,13 @@ export default class Config extends Command {
|
|
|
20
21
|
// name = name.charAt(0).toUpperCase() + name.slice(1);
|
|
21
22
|
const answer = await select({
|
|
22
23
|
choices: [
|
|
23
|
-
{ name: 'External IP Address', value: 'externalIp' },
|
|
24
|
-
{ name: `Discord Alerts`, value: 'discordAlerts' },
|
|
25
|
-
{ name: 'Java Memory', value: 'javaMemory' },
|
|
26
24
|
{ name: 'Key File', value: 'keyFile' },
|
|
25
|
+
{ name: 'Java Memory', value: 'javaMemory' },
|
|
26
|
+
{ name: `Discord Alerts`, value: 'discordAlerts' },
|
|
27
|
+
{ name: `Delegated Staking`, value: 'delegatedStaking' },
|
|
27
28
|
{ name: 'Layers To Run', value: 'layersToRun' },
|
|
28
|
-
{ name:
|
|
29
|
+
{ name: 'Constellation Network', value: 'network' },
|
|
30
|
+
{ name: 'External IP Address', value: 'externalIp' },
|
|
29
31
|
],
|
|
30
32
|
message: 'What would you like to change?:',
|
|
31
33
|
});
|
|
@@ -36,6 +38,9 @@ export default class Config extends Command {
|
|
|
36
38
|
else if (answer === 'discordAlerts') {
|
|
37
39
|
await checkNodePilot.promptDiscordRegistration();
|
|
38
40
|
}
|
|
41
|
+
else if (answer === 'delegatedStaking') {
|
|
42
|
+
await delegatedStakingService.configureNodeParams();
|
|
43
|
+
}
|
|
39
44
|
else if (answer === 'javaMemory') {
|
|
40
45
|
await promptHelper.shutdownNodeIfRunning();
|
|
41
46
|
await checkProject.configureJavaMemoryArguments();
|
package/dist/commands/test.js
CHANGED
|
@@ -4,7 +4,7 @@ import fs from "node:fs";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { getRandomNode } from "../services/get-random-node.js";
|
|
7
|
-
import {
|
|
7
|
+
import { checkDiskSpace } from "../checks/check-disk-space.js";
|
|
8
8
|
export default class Test extends Command {
|
|
9
9
|
static description = 'node pilot test command';
|
|
10
10
|
static hidden = true;
|
|
@@ -18,7 +18,10 @@ export default class Test extends Command {
|
|
|
18
18
|
//
|
|
19
19
|
// await archiverService.syncToLatestSnapshot();
|
|
20
20
|
// await archiverService.checkLogsForMissingSnapshots();
|
|
21
|
-
await StatusTable.run();
|
|
21
|
+
// await StatusTable.run();
|
|
22
|
+
await checkDiskSpace.checkDiskUsage();
|
|
23
|
+
// await delegatedStakingService.postNodeParams(0.08, "Doc Holliday", 'Come and get "som\'e" rewards!', {hash: "0000000000000000000000000000000000000000000000000000000000000000", ordinal: 123_456});
|
|
24
|
+
// await delegatedStakingService.configureNodeParams();
|
|
22
25
|
// console.log(semver.parse("0.11.0-testnet")?.compare('0.11.0'));
|
|
23
26
|
// await checkNodePilot.checkVersion();
|
|
24
27
|
}
|
|
@@ -29,7 +29,7 @@ export const projectHelper = {
|
|
|
29
29
|
}
|
|
30
30
|
if (deleteJars) {
|
|
31
31
|
// eslint-disable-next-line no-await-in-loop
|
|
32
|
-
await shellService.runProjectCommand(`sudo rm -rf
|
|
32
|
+
await shellService.runProjectCommand(`sudo rm -rf dist`);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
clm.postStep('Cleanup complete');
|
|
@@ -110,7 +110,7 @@ export const statusTableHeader = [
|
|
|
110
110
|
{ color: 'white', formatter: formatState, headerColor: 'whiteBright', value: 'Cluster State', width: 16 },
|
|
111
111
|
{ color: 'white', formatter: formatCpu, headerColor: 'whiteBright', value: 'CPU Usage', width: 12 },
|
|
112
112
|
{ color: 'white', formatter: formatMem, headerColor: 'whiteBright', value: 'Mem Usage', width: 12 },
|
|
113
|
-
{ color: 'white', formatter: formatError, headerColor: 'whiteBright', value: 'Error', width: 22 },
|
|
113
|
+
{ color: 'white', formatter: formatError, headerColor: 'whiteBright', value: 'Last Error', width: 22 },
|
|
114
114
|
];
|
|
115
115
|
export const glHeader1 = [
|
|
116
116
|
// { color: 'white', headerColor: 'whiteBright', value: 'Network' },
|
|
@@ -124,5 +124,5 @@ export const glHeader2 = [
|
|
|
124
124
|
{ color: 'white', formatter: formatState, headerColor: 'whiteBright', value: 'Cluster State', width: 22 },
|
|
125
125
|
{ color: 'white', formatter: formatCpu, headerColor: 'whiteBright', value: 'CPU Usage', width: 13 },
|
|
126
126
|
{ color: 'white', formatter: formatMem, headerColor: 'whiteBright', value: 'Mem Usage', width: 16 },
|
|
127
|
-
{ color: 'white', formatter: formatError, headerColor: 'whiteBright', value: 'Error', width: 22 },
|
|
127
|
+
{ color: 'white', formatter: formatError, headerColor: 'whiteBright', value: 'Last Error', width: 22 },
|
|
128
128
|
];
|
|
@@ -5,10 +5,13 @@ export declare const clusterService: {
|
|
|
5
5
|
getClusterNodeInfo(layer?: TessellationLayer): Promise<NodeInfo>;
|
|
6
6
|
getLatestConsensusInfo(layer?: TessellationLayer): Promise<ClusterConsensusInfo>;
|
|
7
7
|
getLayer0(): "gl0" | "ml0";
|
|
8
|
+
getNodeParams(id: string): Promise<any>;
|
|
8
9
|
getReleaseVersion(): Promise<string>;
|
|
9
10
|
getSourceNodeInfo(layer: TessellationLayer): Promise<NodeInfo>;
|
|
10
11
|
getSourceNodeLatestOrdinal(layer: TessellationLayer): Promise<number>;
|
|
11
12
|
getSourceNodeOrdinalHash(layer: TessellationLayer, ordinal: number): Promise<string>;
|
|
12
|
-
|
|
13
|
+
makeClusterRequestGet(path: string, layer?: TessellationLayer): Promise<any>;
|
|
14
|
+
makeClusterRequestPost(path: string, body: string, layer?: TessellationLayer): Promise<any>;
|
|
13
15
|
makeSourceNodeRequest(path: string, layer: TessellationLayer): Promise<any>;
|
|
16
|
+
postNodeParams(body: string, layer?: TessellationLayer): Promise<string>;
|
|
14
17
|
};
|
|
@@ -30,17 +30,20 @@ export const clusterService = {
|
|
|
30
30
|
spinner.stop();
|
|
31
31
|
},
|
|
32
32
|
async getClusterInfo(layer) {
|
|
33
|
-
return this.
|
|
33
|
+
return this.makeClusterRequestGet('cluster/info', layer);
|
|
34
34
|
},
|
|
35
35
|
async getClusterNodeInfo(layer) {
|
|
36
|
-
return this.
|
|
36
|
+
return this.makeClusterRequestGet('node/info', layer);
|
|
37
37
|
},
|
|
38
38
|
async getLatestConsensusInfo(layer) {
|
|
39
|
-
return this.
|
|
39
|
+
return this.makeClusterRequestGet('consensus/latest/peers', layer);
|
|
40
40
|
},
|
|
41
41
|
getLayer0() {
|
|
42
42
|
return configStore.getProjectInfo().layersToRun.includes('gl0') ? 'gl0' : 'ml0';
|
|
43
43
|
},
|
|
44
|
+
async getNodeParams(id) {
|
|
45
|
+
return this.makeClusterRequestGet(`node-params/${id}`, 'gl0');
|
|
46
|
+
},
|
|
44
47
|
async getReleaseVersion() {
|
|
45
48
|
return this.getClusterNodeInfo().then(i => i.version);
|
|
46
49
|
},
|
|
@@ -53,20 +56,40 @@ export const clusterService = {
|
|
|
53
56
|
async getSourceNodeOrdinalHash(layer, ordinal) {
|
|
54
57
|
return this.makeSourceNodeRequest(`global-snapshots/${ordinal}/hash`, layer);
|
|
55
58
|
},
|
|
56
|
-
async
|
|
59
|
+
async makeClusterRequestGet(path, layer) {
|
|
57
60
|
layer = layer || this.getLayer0();
|
|
58
61
|
const { type } = configStore.getNetworkInfo();
|
|
59
62
|
const envLayerInfo = configStore.getEnvLayerInfo(type, layer);
|
|
60
63
|
if (envLayerInfo.CL_LB) {
|
|
61
64
|
return fetch(`${envLayerInfo.CL_LB}/${path}`)
|
|
62
|
-
.then(res =>
|
|
65
|
+
.then(res => {
|
|
66
|
+
if (res.ok) {
|
|
67
|
+
return res.json();
|
|
68
|
+
}
|
|
69
|
+
if (res.status === 404) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
clm.debug(`Error ${res.status}. statusText: ${res.statusText}`);
|
|
73
|
+
throw new Error('Error');
|
|
74
|
+
})
|
|
63
75
|
.catch(() => {
|
|
64
|
-
clm.debug(`Failed to get node info from ${envLayerInfo.CL_LB}. Attempting source node...`);
|
|
76
|
+
clm.debug(`Failed to get node info from ${envLayerInfo.CL_LB}/${path}. Attempting source node...`);
|
|
65
77
|
return this.makeSourceNodeRequest(path, layer);
|
|
66
78
|
});
|
|
67
79
|
}
|
|
68
80
|
return this.makeSourceNodeRequest(path, layer);
|
|
69
81
|
},
|
|
82
|
+
async makeClusterRequestPost(path, body, layer) {
|
|
83
|
+
layer = layer || this.getLayer0();
|
|
84
|
+
const { type } = configStore.getNetworkInfo();
|
|
85
|
+
const envLayerInfo = configStore.getEnvLayerInfo(type, layer);
|
|
86
|
+
return fetch(`${envLayerInfo.CL_LB}/${path}`, {
|
|
87
|
+
body,
|
|
88
|
+
headers: { 'Content-Type': 'application/json' },
|
|
89
|
+
method: 'POST'
|
|
90
|
+
})
|
|
91
|
+
.then(res => res.json());
|
|
92
|
+
},
|
|
70
93
|
async makeSourceNodeRequest(path, layer) {
|
|
71
94
|
const { type } = configStore.getNetworkInfo();
|
|
72
95
|
const { CL_PUBLIC_HTTP_PORT } = configStore.getEnvLayerInfo(type, layer);
|
|
@@ -77,5 +100,8 @@ export const clusterService = {
|
|
|
77
100
|
.catch(() => {
|
|
78
101
|
throw new Error(`Unable to connect to source node at http://${CL_L0_PEER_HTTP_HOST}:${CL_PUBLIC_HTTP_PORT}/${path}`);
|
|
79
102
|
});
|
|
103
|
+
},
|
|
104
|
+
async postNodeParams(body, layer) {
|
|
105
|
+
return this.makeClusterRequestPost(`node-params`, body, layer);
|
|
80
106
|
}
|
|
81
107
|
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare const delegatedStakingService: {
|
|
2
|
+
configureNodeParams(): Promise<void>;
|
|
3
|
+
generateNodeParamPayload(rewardFraction: number, name: string, description: string, lastRef: {
|
|
4
|
+
hash: string;
|
|
5
|
+
ordinal: number;
|
|
6
|
+
}): Promise<string>;
|
|
7
|
+
getNodeParams(): Promise<{
|
|
8
|
+
description: string;
|
|
9
|
+
lastRef: {
|
|
10
|
+
hash: string;
|
|
11
|
+
ordinal: number;
|
|
12
|
+
};
|
|
13
|
+
name: string;
|
|
14
|
+
rewardFraction: number;
|
|
15
|
+
reward?: undefined;
|
|
16
|
+
} | {
|
|
17
|
+
description: string;
|
|
18
|
+
lastRef: {
|
|
19
|
+
hash: string;
|
|
20
|
+
ordinal: number;
|
|
21
|
+
};
|
|
22
|
+
name: string;
|
|
23
|
+
reward: number;
|
|
24
|
+
rewardFraction?: undefined;
|
|
25
|
+
}>;
|
|
26
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { input } from "@inquirer/prompts";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { clm } from "../clm.js";
|
|
6
|
+
import { configStore } from "../config-store.js";
|
|
7
|
+
import { clusterService } from "./cluster-service.js";
|
|
8
|
+
import { shellService } from "./shell-service.js";
|
|
9
|
+
export const delegatedStakingService = {
|
|
10
|
+
async configureNodeParams() {
|
|
11
|
+
const np = await this.getNodeParams();
|
|
12
|
+
if (np.name) {
|
|
13
|
+
clm.postStep(String('\nEdit node parameters:\n\n Name: ' + chalk.cyan(np.name) + '\n Description: ' + chalk.cyan(np.description) + '\n Reward: ' + chalk.cyan(np.reward + '%') + "\n"));
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
clm.postStep(String('------------------------------\nDelegated Staking Registration\n------------------------------\nFill out the parameters to enable delegation to your Validator Node:\n'));
|
|
17
|
+
}
|
|
18
|
+
const name = await input({
|
|
19
|
+
default: np.name, message: 'Name (5-140 chars):',
|
|
20
|
+
required: true, validate: (value) => value.length > 5 && value.length < 140
|
|
21
|
+
});
|
|
22
|
+
const description = await input({
|
|
23
|
+
default: np.description, message: 'Description (5-140 chars):',
|
|
24
|
+
required: true, validate: (value) => value.length > 5 && value.length < 140
|
|
25
|
+
});
|
|
26
|
+
const reward = await input({
|
|
27
|
+
default: String(np.reward), message: 'Commission % (5-10):', required: true,
|
|
28
|
+
validate: (value) => Number(value) >= 5 && Number(value) <= 10
|
|
29
|
+
});
|
|
30
|
+
const rewardFraction = Math.floor(Number(reward) * 1_000_000) / 100_000_000;
|
|
31
|
+
const payload = await this.generateNodeParamPayload(rewardFraction, name, description, np.lastRef);
|
|
32
|
+
// console.log(JSON.stringify(JSON.parse(payload),null,2));
|
|
33
|
+
const hash = await clusterService.postNodeParams(payload, 'gl0');
|
|
34
|
+
console.log(hash);
|
|
35
|
+
const { type } = configStore.getNetworkInfo();
|
|
36
|
+
clm.postStep(`Delegated Staking Page: https://${type}.dagexplorer.io/staking`);
|
|
37
|
+
},
|
|
38
|
+
async generateNodeParamPayload(rewardFraction, name, description, lastRef) {
|
|
39
|
+
name = name.replaceAll(/['"]/g, match => `\\${match}`);
|
|
40
|
+
description = description.replaceAll(/['"]/g, match => `\\${match}`);
|
|
41
|
+
rewardFraction = Math.max(.05, Math.min(.1, rewardFraction));
|
|
42
|
+
const env = configStore.getEnvInfo();
|
|
43
|
+
const { projectDir } = configStore.getProjectInfo();
|
|
44
|
+
fs.writeFileSync(path.join(projectDir, 'parent.json'), JSON.stringify(lastRef));
|
|
45
|
+
const command = `java -jar dist/wallet.jar create-node-params --reward-fraction ${rewardFraction} --name $'${name}' --description $'${description}' -p 'parent.json'`;
|
|
46
|
+
await shellService.runProjectCommand(command, env);
|
|
47
|
+
return fs.readFileSync(path.join(projectDir, 'event'), 'utf8');
|
|
48
|
+
},
|
|
49
|
+
async getNodeParams() {
|
|
50
|
+
const { nodeId } = configStore.getProjectInfo();
|
|
51
|
+
return clusterService.getNodeParams(nodeId)
|
|
52
|
+
.then((params) => {
|
|
53
|
+
if (!params) {
|
|
54
|
+
return { description: '', lastRef: { hash: '0000000000000000000000000000000000000000000000000000000000000000', ordinal: 0 }, name: '', rewardFraction: 5 };
|
|
55
|
+
}
|
|
56
|
+
// console.log(JSON.stringify(params,null,2));
|
|
57
|
+
return {
|
|
58
|
+
description: params.latest.value.nodeMetadataParameters.description,
|
|
59
|
+
lastRef: params.lastRef,
|
|
60
|
+
name: params.latest.value.nodeMetadataParameters.name,
|
|
61
|
+
reward: params.latest.value.delegatedStakeRewardParameters.rewardFraction / 1_000_000
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
};
|
|
@@ -7,6 +7,7 @@ export const migrationService = {
|
|
|
7
7
|
const migrations = {
|
|
8
8
|
'0.12.5': m0125,
|
|
9
9
|
'0.13.9': m0139,
|
|
10
|
+
'0.14.0-intnet.1': m0140intnet1,
|
|
10
11
|
// add more migrations as needed
|
|
11
12
|
};
|
|
12
13
|
const { version = '0.0.0' } = configStore.getProjectInfo();
|
|
@@ -31,6 +32,9 @@ export const migrationService = {
|
|
|
31
32
|
configStore.setProjectInfo({ version: currentVersion.toString() });
|
|
32
33
|
}
|
|
33
34
|
};
|
|
35
|
+
function m0140intnet1() {
|
|
36
|
+
configStore.setProjectFlag('javaMemoryChecked', false);
|
|
37
|
+
}
|
|
34
38
|
function m0139() {
|
|
35
39
|
clm.step('Running migration 0.13.9...');
|
|
36
40
|
configStore.setProjectFlag('javaMemoryChecked', false);
|
|
@@ -8,7 +8,7 @@ export const systemdService = {
|
|
|
8
8
|
async install() {
|
|
9
9
|
const platform = os.platform();
|
|
10
10
|
if (platform !== 'linux') {
|
|
11
|
-
clm.warn('
|
|
11
|
+
clm.warn('systemd services can only be installed on Linux systems. Skipping...\n');
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
14
|
const scriptFile = path.resolve(path.dirname(fileURLToPath(import.meta.url)), `../../scripts/install_services.sh`);
|
|
@@ -22,7 +22,7 @@ export const systemdService = {
|
|
|
22
22
|
async uninstall() {
|
|
23
23
|
const platform = os.platform();
|
|
24
24
|
if (platform !== 'linux') {
|
|
25
|
-
clm.warn('
|
|
25
|
+
clm.warn('systemd services can only be installed on Linux systems. Skipping...\n');
|
|
26
26
|
}
|
|
27
27
|
const scriptFile = path.resolve(path.dirname(fileURLToPath(import.meta.url)), `../../scripts/uninstall_services.sh`);
|
|
28
28
|
if (!fs.existsSync(scriptFile)) {
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@constellation-network/node-pilot",
|
|
3
3
|
"description": "An easy deployment and monitoring tool for Constellation nodes.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.16.0-intnet",
|
|
5
5
|
"author": "Frank Fox",
|
|
6
6
|
"bin": {
|
|
7
7
|
"cpilot": "bin/run.js"
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"version": "oclif readme && git add README.md",
|
|
22
22
|
"start": "./bin/dev.js",
|
|
23
23
|
"debug": "DEBUG=true ./bin/dev.js",
|
|
24
|
-
"pub": "npm publish --access public --tag
|
|
24
|
+
"pub": "npm publish --access public --tag intnet"
|
|
25
25
|
},
|
|
26
26
|
"types": "dist/index.d.ts",
|
|
27
27
|
"engines": {
|
|
@@ -39,7 +39,7 @@ COPY dist/gl0.jar /app/jars/gl0.jar
|
|
|
39
39
|
#COPY ./health-check /health-check
|
|
40
40
|
#RUN chmod +x /health-check/bin/run.sh
|
|
41
41
|
#RUN chmod +x /health-check/bin/hydrate.sh
|
|
42
|
-
RUN npm install -g "@constellation-network/node-pilot-health-check@0.0.
|
|
42
|
+
RUN npm install -g "@constellation-network/node-pilot-health-check@0.0.31"
|
|
43
43
|
|
|
44
44
|
# Add entrypoint
|
|
45
45
|
COPY ./entrypoint.sh /app/entrypoint.sh
|