@chainsafe/lodestar 1.35.0-dev.a70bac5bd3 → 1.35.0-dev.ba92bd8a88
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/.git-data.json +1 -1
- package/bin/lodestar.js +3 -0
- package/bin/lodestar.ts +3 -0
- package/lib/applyPreset.d.ts.map +1 -0
- package/lib/cli.d.ts +3 -3
- package/lib/cli.d.ts.map +1 -0
- package/lib/cli.js +1 -1
- package/lib/cli.js.map +1 -1
- package/lib/cmds/beacon/handler.d.ts +1 -1
- package/lib/cmds/beacon/handler.d.ts.map +1 -0
- package/lib/cmds/beacon/handler.js +1 -1
- package/lib/cmds/beacon/handler.js.map +1 -1
- package/lib/cmds/beacon/index.d.ts.map +1 -0
- package/lib/cmds/beacon/initBeaconState.d.ts.map +1 -0
- package/lib/cmds/beacon/initBeaconState.js.map +1 -1
- package/lib/cmds/beacon/initPeerIdAndEnr.d.ts +2 -2
- package/lib/cmds/beacon/initPeerIdAndEnr.d.ts.map +1 -0
- package/lib/cmds/beacon/initPeerIdAndEnr.js +1 -1
- package/lib/cmds/beacon/initPeerIdAndEnr.js.map +1 -1
- package/lib/cmds/beacon/options.d.ts.map +1 -0
- package/lib/cmds/beacon/paths.d.ts.map +1 -0
- package/lib/cmds/bootnode/handler.d.ts +13 -8
- package/lib/cmds/bootnode/handler.d.ts.map +1 -0
- package/lib/cmds/bootnode/handler.js +2 -2
- package/lib/cmds/bootnode/handler.js.map +1 -1
- package/lib/cmds/bootnode/index.d.ts.map +1 -0
- package/lib/cmds/bootnode/options.d.ts.map +1 -0
- package/lib/cmds/bootnode/options.js +2 -1
- package/lib/cmds/bootnode/options.js.map +1 -1
- package/lib/cmds/dev/files.d.ts.map +1 -0
- package/lib/cmds/dev/handler.d.ts.map +1 -0
- package/lib/cmds/dev/handler.js +1 -1
- package/lib/cmds/dev/handler.js.map +1 -1
- package/lib/cmds/dev/index.d.ts.map +1 -0
- package/lib/cmds/dev/options.d.ts.map +1 -0
- package/lib/cmds/index.d.ts.map +1 -0
- package/lib/cmds/lightclient/handler.d.ts.map +1 -0
- package/lib/cmds/lightclient/index.d.ts.map +1 -0
- package/lib/cmds/lightclient/options.d.ts.map +1 -0
- package/lib/cmds/validator/blsToExecutionChange.d.ts.map +1 -0
- package/lib/cmds/validator/blsToExecutionChange.js.map +1 -1
- package/lib/cmds/validator/handler.d.ts.map +1 -0
- package/lib/cmds/validator/handler.js +3 -5
- package/lib/cmds/validator/handler.js.map +1 -1
- package/lib/cmds/validator/import.d.ts.map +1 -0
- package/lib/cmds/validator/index.d.ts.map +1 -0
- package/lib/cmds/validator/keymanager/decryptKeystoreDefinitions.d.ts.map +1 -0
- package/lib/cmds/validator/keymanager/decryptKeystores/index.d.ts.map +1 -0
- package/lib/cmds/validator/keymanager/decryptKeystores/poolSize.d.ts.map +1 -0
- package/lib/cmds/validator/keymanager/decryptKeystores/threadPool.d.ts.map +1 -0
- package/lib/cmds/validator/keymanager/decryptKeystores/threadPool.js +4 -1
- package/lib/cmds/validator/keymanager/decryptKeystores/threadPool.js.map +1 -1
- package/lib/cmds/validator/keymanager/decryptKeystores/types.d.ts.map +1 -0
- package/lib/cmds/validator/keymanager/decryptKeystores/worker.d.ts.map +1 -0
- package/lib/cmds/validator/keymanager/impl.d.ts.map +1 -0
- package/lib/cmds/validator/keymanager/impl.js +4 -0
- package/lib/cmds/validator/keymanager/impl.js.map +1 -1
- package/lib/cmds/validator/keymanager/interface.d.ts.map +1 -0
- package/lib/cmds/validator/keymanager/keystoreCache.d.ts.map +1 -0
- package/lib/cmds/validator/keymanager/persistedKeys.d.ts.map +1 -0
- package/lib/cmds/validator/keymanager/persistedKeys.js +1 -0
- package/lib/cmds/validator/keymanager/persistedKeys.js.map +1 -1
- package/lib/cmds/validator/keymanager/server.d.ts.map +1 -0
- package/lib/cmds/validator/keymanager/server.js +2 -0
- package/lib/cmds/validator/keymanager/server.js.map +1 -1
- package/lib/cmds/validator/list.d.ts.map +1 -0
- package/lib/cmds/validator/options.d.ts.map +1 -0
- package/lib/cmds/validator/paths.d.ts.map +1 -0
- package/lib/cmds/validator/signers/importExternalKeystores.d.ts.map +1 -0
- package/lib/cmds/validator/signers/index.d.ts.map +1 -0
- package/lib/cmds/validator/signers/logSigners.d.ts.map +1 -0
- package/lib/cmds/validator/slashingProtection/export.d.ts.map +1 -0
- package/lib/cmds/validator/slashingProtection/import.d.ts.map +1 -0
- package/lib/cmds/validator/slashingProtection/index.d.ts.map +1 -0
- package/lib/cmds/validator/slashingProtection/options.d.ts.map +1 -0
- package/lib/cmds/validator/slashingProtection/utils.d.ts.map +1 -0
- package/lib/cmds/validator/slashingProtection/utils.js +1 -1
- package/lib/cmds/validator/slashingProtection/utils.js.map +1 -1
- package/lib/cmds/validator/voluntaryExit.d.ts.map +1 -0
- package/lib/cmds/validator/voluntaryExit.js +1 -1
- package/lib/cmds/validator/voluntaryExit.js.map +1 -1
- package/lib/config/beaconNodeOptions.d.ts.map +1 -0
- package/lib/config/beaconNodeOptions.js +2 -1
- package/lib/config/beaconNodeOptions.js.map +1 -1
- package/lib/config/beaconParams.d.ts.map +1 -0
- package/lib/config/index.d.ts.map +1 -0
- package/lib/config/peerId.d.ts.map +1 -0
- package/lib/config/types.d.ts.map +1 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js.map +1 -1
- package/lib/migrations/index.d.ts +1 -0
- package/lib/migrations/index.d.ts.map +1 -0
- package/lib/migrations/index.js +1 -1
- package/lib/networks/chiado.d.ts.map +1 -0
- package/lib/networks/dev.d.ts.map +1 -0
- package/lib/networks/ephemery.d.ts.map +1 -0
- package/lib/networks/gnosis.d.ts.map +1 -0
- package/lib/networks/holesky.d.ts.map +1 -0
- package/lib/networks/hoodi.d.ts.map +1 -0
- package/lib/networks/index.d.ts.map +1 -0
- package/lib/networks/index.js +1 -2
- package/lib/networks/index.js.map +1 -1
- package/lib/networks/mainnet.d.ts.map +1 -0
- package/lib/networks/sepolia.d.ts.map +1 -0
- package/lib/options/beaconNodeOptions/api.d.ts.map +1 -0
- package/lib/options/beaconNodeOptions/builder.d.ts.map +1 -0
- package/lib/options/beaconNodeOptions/chain.d.ts.map +1 -0
- package/lib/options/beaconNodeOptions/eth1.d.ts.map +1 -0
- package/lib/options/beaconNodeOptions/execution.d.ts.map +1 -0
- package/lib/options/beaconNodeOptions/index.d.ts.map +1 -0
- package/lib/options/beaconNodeOptions/metrics.d.ts.map +1 -0
- package/lib/options/beaconNodeOptions/monitoring.d.ts.map +1 -0
- package/lib/options/beaconNodeOptions/network.d.ts +1 -0
- package/lib/options/beaconNodeOptions/network.d.ts.map +1 -0
- package/lib/options/beaconNodeOptions/network.js +6 -3
- package/lib/options/beaconNodeOptions/network.js.map +1 -1
- package/lib/options/beaconNodeOptions/sync.d.ts.map +1 -0
- package/lib/options/globalOptions.d.ts.map +1 -0
- package/lib/options/index.d.ts.map +1 -0
- package/lib/options/logOptions.d.ts.map +1 -0
- package/lib/options/paramsOptions.d.ts.map +1 -0
- package/lib/paths/global.d.ts.map +1 -0
- package/lib/paths/rootDir.d.ts.map +1 -0
- package/lib/util/errors.d.ts.map +1 -0
- package/lib/util/ethers.d.ts.map +1 -0
- package/lib/util/feeRecipient.d.ts.map +1 -0
- package/lib/util/file.d.ts.map +1 -0
- package/lib/util/file.js +1 -1
- package/lib/util/file.js.map +1 -1
- package/lib/util/format.d.ts.map +1 -0
- package/lib/util/fs.d.ts.map +1 -0
- package/lib/util/gitData/gitDataPath.d.ts.map +1 -0
- package/lib/util/gitData/index.d.ts.map +1 -0
- package/lib/util/gitData/index.js.map +1 -1
- package/lib/util/gitData/writeGitData.d.ts.map +1 -0
- package/lib/util/index.d.ts +3 -3
- package/lib/util/index.d.ts.map +1 -0
- package/lib/util/index.js +3 -3
- package/lib/util/index.js.map +1 -1
- package/lib/util/jwt.d.ts.map +1 -0
- package/lib/util/lockfile.d.ts.map +1 -0
- package/lib/util/logger.d.ts.map +1 -0
- package/lib/util/object.d.ts.map +1 -0
- package/lib/util/object.js.map +1 -1
- package/lib/util/passphrase.d.ts.map +1 -0
- package/lib/util/process.d.ts.map +1 -0
- package/lib/util/progress.d.ts.map +1 -0
- package/lib/util/proposerConfig.d.ts.map +1 -0
- package/lib/util/proposerConfig.js.map +1 -1
- package/lib/util/pruneOldFilesInDir.d.ts.map +1 -0
- package/lib/util/sleep.d.ts.map +1 -0
- package/lib/util/stripOffNewlines.d.ts.map +1 -0
- package/lib/util/types.d.ts.map +1 -0
- package/lib/util/version.d.ts.map +1 -0
- package/package.json +20 -19
- package/src/applyPreset.ts +91 -0
- package/src/cli.ts +56 -0
- package/src/cmds/beacon/handler.ts +267 -0
- package/src/cmds/beacon/index.ts +18 -0
- package/src/cmds/beacon/initBeaconState.ts +275 -0
- package/src/cmds/beacon/initPeerIdAndEnr.ts +199 -0
- package/src/cmds/beacon/options.ts +214 -0
- package/src/cmds/beacon/paths.ts +62 -0
- package/src/cmds/bootnode/handler.ts +203 -0
- package/src/cmds/bootnode/index.ts +13 -0
- package/src/cmds/bootnode/options.ts +109 -0
- package/src/cmds/dev/files.ts +52 -0
- package/src/cmds/dev/handler.ts +86 -0
- package/src/cmds/dev/index.ts +18 -0
- package/src/cmds/dev/options.ts +110 -0
- package/src/cmds/index.ts +15 -0
- package/src/cmds/lightclient/handler.ts +36 -0
- package/src/cmds/lightclient/index.ts +18 -0
- package/src/cmds/lightclient/options.ts +21 -0
- package/src/cmds/validator/blsToExecutionChange.ts +91 -0
- package/src/cmds/validator/handler.ts +300 -0
- package/src/cmds/validator/import.ts +111 -0
- package/src/cmds/validator/index.ts +28 -0
- package/src/cmds/validator/keymanager/decryptKeystoreDefinitions.ts +189 -0
- package/src/cmds/validator/keymanager/decryptKeystores/index.ts +1 -0
- package/src/cmds/validator/keymanager/decryptKeystores/poolSize.ts +16 -0
- package/src/cmds/validator/keymanager/decryptKeystores/threadPool.ts +75 -0
- package/src/cmds/validator/keymanager/decryptKeystores/types.ts +12 -0
- package/src/cmds/validator/keymanager/decryptKeystores/worker.ts +24 -0
- package/src/cmds/validator/keymanager/impl.ts +425 -0
- package/src/cmds/validator/keymanager/interface.ts +35 -0
- package/src/cmds/validator/keymanager/keystoreCache.ts +91 -0
- package/src/cmds/validator/keymanager/persistedKeys.ts +268 -0
- package/src/cmds/validator/keymanager/server.ts +86 -0
- package/src/cmds/validator/list.ts +35 -0
- package/src/cmds/validator/options.ts +461 -0
- package/src/cmds/validator/paths.ts +95 -0
- package/src/cmds/validator/signers/importExternalKeystores.ts +69 -0
- package/src/cmds/validator/signers/index.ts +176 -0
- package/src/cmds/validator/signers/logSigners.ts +81 -0
- package/src/cmds/validator/slashingProtection/export.ts +110 -0
- package/src/cmds/validator/slashingProtection/import.ts +70 -0
- package/src/cmds/validator/slashingProtection/index.ts +12 -0
- package/src/cmds/validator/slashingProtection/options.ts +15 -0
- package/src/cmds/validator/slashingProtection/utils.ts +56 -0
- package/src/cmds/validator/voluntaryExit.ts +232 -0
- package/src/config/beaconNodeOptions.ts +68 -0
- package/src/config/beaconParams.ts +87 -0
- package/src/config/index.ts +3 -0
- package/src/config/peerId.ts +50 -0
- package/src/config/types.ts +3 -0
- package/src/index.ts +28 -0
- package/src/migrations/index.ts +0 -0
- package/src/networks/chiado.ts +20 -0
- package/src/networks/dev.ts +27 -0
- package/src/networks/ephemery.ts +9 -0
- package/src/networks/gnosis.ts +18 -0
- package/src/networks/holesky.ts +17 -0
- package/src/networks/hoodi.ts +16 -0
- package/src/networks/index.ts +236 -0
- package/src/networks/mainnet.ts +34 -0
- package/src/networks/sepolia.ts +17 -0
- package/src/options/beaconNodeOptions/api.ts +110 -0
- package/src/options/beaconNodeOptions/builder.ts +63 -0
- package/src/options/beaconNodeOptions/chain.ts +326 -0
- package/src/options/beaconNodeOptions/eth1.ts +95 -0
- package/src/options/beaconNodeOptions/execution.ts +92 -0
- package/src/options/beaconNodeOptions/index.ts +50 -0
- package/src/options/beaconNodeOptions/metrics.ts +39 -0
- package/src/options/beaconNodeOptions/monitoring.ts +61 -0
- package/src/options/beaconNodeOptions/network.ts +401 -0
- package/src/options/beaconNodeOptions/sync.ts +65 -0
- package/src/options/globalOptions.ts +72 -0
- package/src/options/index.ts +3 -0
- package/src/options/logOptions.ts +70 -0
- package/src/options/paramsOptions.ts +72 -0
- package/src/paths/global.ts +24 -0
- package/src/paths/rootDir.ts +11 -0
- package/src/util/errors.ts +20 -0
- package/src/util/ethers.ts +44 -0
- package/src/util/feeRecipient.ts +6 -0
- package/src/util/file.ts +167 -0
- package/src/util/format.ts +76 -0
- package/src/util/fs.ts +59 -0
- package/src/util/gitData/gitDataPath.ts +48 -0
- package/src/util/gitData/index.ts +70 -0
- package/src/util/gitData/writeGitData.ts +10 -0
- package/src/util/index.ts +17 -0
- package/src/util/jwt.ts +10 -0
- package/src/util/lockfile.ts +45 -0
- package/src/util/logger.ts +105 -0
- package/src/util/object.ts +15 -0
- package/src/util/passphrase.ts +25 -0
- package/src/util/process.ts +25 -0
- package/src/util/progress.ts +58 -0
- package/src/util/proposerConfig.ts +136 -0
- package/src/util/pruneOldFilesInDir.ts +27 -0
- package/src/util/sleep.ts +3 -0
- package/src/util/stripOffNewlines.ts +6 -0
- package/src/util/types.ts +8 -0
- package/src/util/version.ts +74 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {ChainForkConfig} from "@lodestar/config";
|
|
4
|
+
import {LogFormat, TimestampFormatCode, logFormats} from "@lodestar/logger";
|
|
5
|
+
import {LoggerNodeOpts} from "@lodestar/logger/node";
|
|
6
|
+
import {SLOTS_PER_EPOCH} from "@lodestar/params";
|
|
7
|
+
import {LogLevel} from "@lodestar/utils";
|
|
8
|
+
import {GlobalArgs} from "../options/globalOptions.js";
|
|
9
|
+
import {LogArgs} from "../options/logOptions.js";
|
|
10
|
+
|
|
11
|
+
export const LOG_FILE_DISABLE_KEYWORD = "none";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Setup a CLI logger, common for beacon, validator and dev commands
|
|
15
|
+
*/
|
|
16
|
+
export function parseLoggerArgs(
|
|
17
|
+
args: LogArgs & Pick<GlobalArgs, "dataDir">,
|
|
18
|
+
paths: {defaultLogFilepath: string},
|
|
19
|
+
config: ChainForkConfig,
|
|
20
|
+
opts?: {hideTimestamp?: boolean}
|
|
21
|
+
): LoggerNodeOpts {
|
|
22
|
+
return {
|
|
23
|
+
level: parseLogLevel(args.logLevel),
|
|
24
|
+
file:
|
|
25
|
+
args.logFile === LOG_FILE_DISABLE_KEYWORD
|
|
26
|
+
? undefined
|
|
27
|
+
: {
|
|
28
|
+
filepath: args.logFile ?? paths.defaultLogFilepath,
|
|
29
|
+
level: parseLogLevel(args.logFileLevel),
|
|
30
|
+
dailyRotate: args.logFileDailyRotate,
|
|
31
|
+
},
|
|
32
|
+
module: args.logPrefix,
|
|
33
|
+
format: args.logFormat ? parseLogFormat(args.logFormat) : undefined,
|
|
34
|
+
levelModule: args.logLevelModule && parseLogLevelModule(args.logLevelModule),
|
|
35
|
+
timestampFormat: opts?.hideTimestamp
|
|
36
|
+
? {format: TimestampFormatCode.Hidden}
|
|
37
|
+
: args.logFormatGenesisTime !== undefined
|
|
38
|
+
? {
|
|
39
|
+
format: TimestampFormatCode.EpochSlot,
|
|
40
|
+
genesisTime: args.logFormatGenesisTime,
|
|
41
|
+
secondsPerSlot: config.SECONDS_PER_SLOT,
|
|
42
|
+
slotsPerEpoch: SLOTS_PER_EPOCH,
|
|
43
|
+
}
|
|
44
|
+
: {
|
|
45
|
+
format: TimestampFormatCode.DateRegular,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function parseLogFormat(format: string): LogFormat {
|
|
51
|
+
if (!logFormats.includes(format as LogFormat)) {
|
|
52
|
+
throw Error(`Unknown log format ${format}`);
|
|
53
|
+
}
|
|
54
|
+
return format as LogFormat;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function parseLogLevel(level: string): LogLevel {
|
|
58
|
+
if (LogLevel[level as LogLevel] === undefined) {
|
|
59
|
+
throw Error(`Unknown log level '${level}'`);
|
|
60
|
+
}
|
|
61
|
+
return level as LogLevel;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function parseLogLevelModule(logLevelModuleArr: string[]): Record<string, LogLevel> {
|
|
65
|
+
const levelModule: Record<string, LogLevel> = {};
|
|
66
|
+
for (const logLevelModule of logLevelModuleArr) {
|
|
67
|
+
const [module, levelStr] = logLevelModule.split("=");
|
|
68
|
+
levelModule[module] = parseLogLevel(levelStr);
|
|
69
|
+
}
|
|
70
|
+
return levelModule;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Winston is not able to clean old log files if server is offline for a while
|
|
75
|
+
* so we have to do this manually when starting the node.
|
|
76
|
+
* See https://github.com/ChainSafe/lodestar/issues/4419
|
|
77
|
+
*/
|
|
78
|
+
export function cleanOldLogFiles(args: LogArgs, paths: {defaultLogFilepath: string}): void {
|
|
79
|
+
const filepath = args.logFile ?? paths.defaultLogFilepath;
|
|
80
|
+
const folder = path.dirname(filepath);
|
|
81
|
+
const filename = path.basename(filepath);
|
|
82
|
+
const lastIndexDot = filename.lastIndexOf(".");
|
|
83
|
+
const prefix = filename.substring(0, lastIndexDot);
|
|
84
|
+
const extension = filename.substring(lastIndexDot + 1, filename.length);
|
|
85
|
+
const toDelete = fs
|
|
86
|
+
.readdirSync(folder, {withFileTypes: true})
|
|
87
|
+
.filter((de) => de.isFile())
|
|
88
|
+
.map((de) => de.name)
|
|
89
|
+
.filter((logFileName) => shouldDeleteLogFile(prefix, extension, logFileName, args.logFileDailyRotate))
|
|
90
|
+
.map((logFileName) => path.join(folder, logFileName));
|
|
91
|
+
// delete files
|
|
92
|
+
for (const filename of toDelete) {
|
|
93
|
+
fs.unlinkSync(filename);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function shouldDeleteLogFile(prefix: string, extension: string, logFileName: string, maxFiles: number): boolean {
|
|
98
|
+
const maxDifferenceMs = maxFiles * 24 * 60 * 60 * 1000;
|
|
99
|
+
const match = logFileName.match(new RegExp(`${prefix}-([0-9]{4}-[0-9]{2}-[0-9]{2}).${extension}`));
|
|
100
|
+
// if match[1] exists, it should be the date pattern of YYYY-MM-DD
|
|
101
|
+
if (match?.[1] && Date.now() - new Date(match[1]).getTime() > maxDifferenceMs) {
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {RecursivePartial} from "@lodestar/utils";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Removes (mutates) all properties with a value === undefined, recursively
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// biome-ignore lint/suspicious/noExplicitAny: We need to use `any` type here
|
|
8
|
+
export function removeUndefinedRecursive<T extends {[key: string]: any}>(obj: T): RecursivePartial<T> {
|
|
9
|
+
for (const key of Object.keys(obj)) {
|
|
10
|
+
const value = obj[key];
|
|
11
|
+
if (value && typeof value === "object") removeUndefinedRecursive(value);
|
|
12
|
+
else if (value === undefined) delete obj[key];
|
|
13
|
+
}
|
|
14
|
+
return obj as RecursivePartial<T>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import {stripOffNewlines} from "./stripOffNewlines.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Utility to read file as UTF8 and strip any trailing new lines.
|
|
6
|
+
* All passphrase files must be read with this function
|
|
7
|
+
*/
|
|
8
|
+
export function readPassphraseFile(passphraseFile: string): string {
|
|
9
|
+
const data = fs.readFileSync(passphraseFile, "utf8");
|
|
10
|
+
const passphrase = stripOffNewlines(data);
|
|
11
|
+
|
|
12
|
+
// Validate the passphraseFile contents to prevent the user to create a wallet with a password
|
|
13
|
+
// that is the contents a random unintended file
|
|
14
|
+
try {
|
|
15
|
+
if (passphrase.includes("\n")) throw Error("contains multiple lines");
|
|
16
|
+
// 512 is an arbitrary high number that should be longer than any actual passphrase
|
|
17
|
+
if (passphrase.length > 512) throw Error("is really long");
|
|
18
|
+
} catch (e) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
`passphraseFile ${passphraseFile} ${(e as Error).message}. Is this a well-formatted passphraseFile?`
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return passphrase;
|
|
25
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const exitSignals = ["SIGTERM", "SIGINT"] as NodeJS.Signals[];
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* All CLI handlers should register this callback to exit properly and not leave
|
|
5
|
+
* a process hanging forever. Pass a clean function that will be run until the
|
|
6
|
+
* user forcibly kills the process by doing CTRL+C again
|
|
7
|
+
* @param cleanUpFunction
|
|
8
|
+
*/
|
|
9
|
+
export function onGracefulShutdown(
|
|
10
|
+
cleanUpFunction: () => Promise<void>,
|
|
11
|
+
logFn: (msg: string) => void = console.log
|
|
12
|
+
): void {
|
|
13
|
+
for (const signal of exitSignals) {
|
|
14
|
+
process.once(signal, async function onSignal() {
|
|
15
|
+
logFn("Stopping gracefully, use Ctrl+C again to force process exit");
|
|
16
|
+
|
|
17
|
+
process.on(signal, function onSecondSignal() {
|
|
18
|
+
logFn("Forcing process exit");
|
|
19
|
+
process.exit(1);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
await cleanUpFunction();
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// zero is considered first index in the range
|
|
2
|
+
type NeedleFunc = (needle: number) => void;
|
|
3
|
+
type ProgressFunc = (opts: {current: number; total: number; percentage: number; ratePerSec: number}) => void;
|
|
4
|
+
|
|
5
|
+
export function showProgress({
|
|
6
|
+
total,
|
|
7
|
+
signal,
|
|
8
|
+
frequencyMs,
|
|
9
|
+
progress,
|
|
10
|
+
}: {
|
|
11
|
+
total: number;
|
|
12
|
+
signal: AbortSignal;
|
|
13
|
+
frequencyMs: number;
|
|
14
|
+
progress: ProgressFunc;
|
|
15
|
+
}): NeedleFunc {
|
|
16
|
+
let current = 0;
|
|
17
|
+
let last = 0;
|
|
18
|
+
let lastProcessTime: number = Date.now();
|
|
19
|
+
let progressIntervalId: NodeJS.Timeout;
|
|
20
|
+
|
|
21
|
+
const needle: NeedleFunc = (needle: number) => {
|
|
22
|
+
// zero is considered first index in the range
|
|
23
|
+
current = needle + 1;
|
|
24
|
+
|
|
25
|
+
if (current >= total) {
|
|
26
|
+
processProgress();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const processProgress = (): void => {
|
|
31
|
+
const currentTime = Date.now();
|
|
32
|
+
const processTime = currentTime - lastProcessTime;
|
|
33
|
+
|
|
34
|
+
progress({
|
|
35
|
+
current,
|
|
36
|
+
total,
|
|
37
|
+
ratePerSec: processTime === 0 ? 0 : ((current - last) / processTime) * 1000,
|
|
38
|
+
percentage: total ? (current / total) * 100 : 100,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
last = current;
|
|
42
|
+
lastProcessTime = currentTime;
|
|
43
|
+
|
|
44
|
+
if (current >= total) {
|
|
45
|
+
clearInterval(progressIntervalId);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
if (total > 0) {
|
|
50
|
+
progressIntervalId = setInterval(processProgress, frequencyMs);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
signal.addEventListener("abort", () => {
|
|
54
|
+
clearInterval(progressIntervalId);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return needle;
|
|
58
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {routes} from "@lodestar/api";
|
|
4
|
+
import {ValidatorProposerConfig} from "@lodestar/validator";
|
|
5
|
+
import {parseFeeRecipient} from "./feeRecipient.js";
|
|
6
|
+
import {readFile} from "./file.js";
|
|
7
|
+
|
|
8
|
+
type ProposerConfig = ValidatorProposerConfig["defaultConfig"];
|
|
9
|
+
|
|
10
|
+
type ProposerConfigFileSection = {
|
|
11
|
+
graffiti?: string;
|
|
12
|
+
strict_fee_recipient_check?: string;
|
|
13
|
+
fee_recipient?: string;
|
|
14
|
+
builder?: {
|
|
15
|
+
// boolean are parse as string by the default schema readFile employs
|
|
16
|
+
// for js-yaml
|
|
17
|
+
gas_limit?: number;
|
|
18
|
+
selection?: routes.validator.BuilderSelection;
|
|
19
|
+
boost_factor?: bigint;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type ProposerConfigFile = {
|
|
24
|
+
proposer_config?: {[index: string]: ProposerConfigFileSection};
|
|
25
|
+
default_config?: ProposerConfigFileSection;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export function parseProposerConfig(
|
|
29
|
+
configFilePath: string,
|
|
30
|
+
defaultArgsConfig?: ProposerConfig
|
|
31
|
+
): ValidatorProposerConfig {
|
|
32
|
+
const configFile = readFile<ProposerConfigFile>(configFilePath, ["yml", "yaml"]);
|
|
33
|
+
const defaultConfigParsed = parseProposerConfigSection(configFile.default_config || {}, defaultArgsConfig);
|
|
34
|
+
|
|
35
|
+
const proposerConfigFile = configFile.proposer_config || {};
|
|
36
|
+
const proposerConfigParsed: ValidatorProposerConfig["proposerConfig"] = {};
|
|
37
|
+
for (const pubkeyHex of Object.keys(proposerConfigFile)) {
|
|
38
|
+
proposerConfigParsed[pubkeyHex] = parseProposerConfigSection(proposerConfigFile[pubkeyHex]);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
proposerConfig: proposerConfigParsed,
|
|
43
|
+
defaultConfig: defaultConfigParsed,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function stringtoBool(input: string): boolean {
|
|
48
|
+
const boolValue = typeof input === "string" ? input === "true" : input;
|
|
49
|
+
return boolValue;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function parseProposerConfigSection(
|
|
53
|
+
proposerFileSection: ProposerConfigFileSection,
|
|
54
|
+
overrideConfig?: ProposerConfig
|
|
55
|
+
): ProposerConfig {
|
|
56
|
+
const {graffiti, strict_fee_recipient_check, fee_recipient, builder} = proposerFileSection;
|
|
57
|
+
const {gas_limit, selection: builderSelection, boost_factor} = builder || {};
|
|
58
|
+
|
|
59
|
+
if (graffiti !== undefined && typeof graffiti !== "string") {
|
|
60
|
+
throw Error("graffiti is not 'string");
|
|
61
|
+
}
|
|
62
|
+
if (
|
|
63
|
+
strict_fee_recipient_check !== undefined &&
|
|
64
|
+
!(strict_fee_recipient_check === "true" || strict_fee_recipient_check === "false")
|
|
65
|
+
) {
|
|
66
|
+
throw Error("strict_fee_recipient_check is not set to boolean");
|
|
67
|
+
}
|
|
68
|
+
if (fee_recipient !== undefined && typeof fee_recipient !== "string") {
|
|
69
|
+
throw Error("fee_recipient is not 'string");
|
|
70
|
+
}
|
|
71
|
+
if (gas_limit !== undefined) {
|
|
72
|
+
if (typeof gas_limit !== "string") {
|
|
73
|
+
throw Error("(typeof gas_limit !== 'string') 2 ");
|
|
74
|
+
}
|
|
75
|
+
if (Number.isNaN(Number(gas_limit))) {
|
|
76
|
+
throw Error("(Number.isNaN(Number(gas_limit)) 2");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (boost_factor !== undefined && typeof boost_factor !== "string") {
|
|
80
|
+
throw Error("boost_factor is not 'string");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
graffiti: overrideConfig?.graffiti ?? graffiti,
|
|
85
|
+
strictFeeRecipientCheck:
|
|
86
|
+
overrideConfig?.strictFeeRecipientCheck ??
|
|
87
|
+
(strict_fee_recipient_check ? stringtoBool(strict_fee_recipient_check) : undefined),
|
|
88
|
+
feeRecipient: overrideConfig?.feeRecipient ?? (fee_recipient ? parseFeeRecipient(fee_recipient) : undefined),
|
|
89
|
+
builder:
|
|
90
|
+
overrideConfig?.builder || builder
|
|
91
|
+
? {
|
|
92
|
+
gasLimit: overrideConfig?.builder?.gasLimit ?? (gas_limit !== undefined ? Number(gas_limit) : undefined),
|
|
93
|
+
selection: overrideConfig?.builder?.selection ?? parseBuilderSelection(builderSelection),
|
|
94
|
+
boostFactor: overrideConfig?.builder?.boostFactor ?? parseBuilderBoostFactor(boost_factor),
|
|
95
|
+
}
|
|
96
|
+
: undefined,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function readProposerConfigDir(filepath: string, filename: string): ProposerConfigFileSection {
|
|
101
|
+
const proposerConfigStr = fs.readFileSync(path.join(filepath, filename), "utf8");
|
|
102
|
+
const proposerConfigJSON = JSON.parse(proposerConfigStr) as ProposerConfigFileSection;
|
|
103
|
+
return proposerConfigJSON;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function parseBuilderSelection(builderSelection?: string): routes.validator.BuilderSelection | undefined {
|
|
107
|
+
if (builderSelection) {
|
|
108
|
+
switch (builderSelection) {
|
|
109
|
+
case "default":
|
|
110
|
+
break;
|
|
111
|
+
case "maxprofit":
|
|
112
|
+
break;
|
|
113
|
+
case "builderalways":
|
|
114
|
+
break;
|
|
115
|
+
case "builderonly":
|
|
116
|
+
break;
|
|
117
|
+
case "executionalways":
|
|
118
|
+
break;
|
|
119
|
+
case "executiononly":
|
|
120
|
+
break;
|
|
121
|
+
default:
|
|
122
|
+
throw Error("Invalid input for builder selection, check help");
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return builderSelection as routes.validator.BuilderSelection;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function parseBuilderBoostFactor(boostFactor?: string): bigint | undefined {
|
|
129
|
+
if (boostFactor === undefined) return;
|
|
130
|
+
|
|
131
|
+
if (!/^\d+$/.test(boostFactor)) {
|
|
132
|
+
throw Error("Invalid input for builder boost factor, must be a valid number without decimals");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return BigInt(boostFactor);
|
|
136
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
export function pruneOldFilesInDir(dirpath: string, maxAgeMs: number): number {
|
|
5
|
+
if (!fs.existsSync(dirpath)) {
|
|
6
|
+
return 0; // Nothing to prune
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let deletedFileCount = 0;
|
|
10
|
+
for (const entryName of fs.readdirSync(dirpath)) {
|
|
11
|
+
const entryPath = path.join(dirpath, entryName);
|
|
12
|
+
|
|
13
|
+
const stat = fs.statSync(entryPath);
|
|
14
|
+
if (stat.isDirectory()) {
|
|
15
|
+
deletedFileCount += pruneOldFilesInDir(entryPath, maxAgeMs);
|
|
16
|
+
} else if (stat.isFile() && Date.now() - stat.mtimeMs > maxAgeMs) {
|
|
17
|
+
fs.unlinkSync(entryPath);
|
|
18
|
+
deletedFileCount += 1;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// if all files are deleted, delete the directory
|
|
23
|
+
if (fs.readdirSync(dirpath).length === 0) {
|
|
24
|
+
fs.rmdirSync(dirpath);
|
|
25
|
+
}
|
|
26
|
+
return deletedFileCount;
|
|
27
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {fileURLToPath} from "node:url";
|
|
4
|
+
import {findUpSync} from "find-up";
|
|
5
|
+
import {readAndGetGitData} from "./gitData/index.js";
|
|
6
|
+
|
|
7
|
+
// Global variable __dirname no longer available in ES6 modules.
|
|
8
|
+
// Solutions: https://stackoverflow.com/questions/46745014/alternative-for-dirname-in-node-js-when-using-es6-modules
|
|
9
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
|
|
11
|
+
type VersionJson = {
|
|
12
|
+
/** "0.28.2" */
|
|
13
|
+
version: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const BRANCH_IGNORE = /^(HEAD|master|unstable|main)$/;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Gathers all information on package version including Git data.
|
|
20
|
+
* @returns a version string, e.g.
|
|
21
|
+
* - Stable release: `v0.36.0/80c248bb`
|
|
22
|
+
* - Dev release: `v0.36.0-dev.80c248bb/80c248bb`
|
|
23
|
+
* - Test branch: `v0.36.0/developer-feature/80c248bb`
|
|
24
|
+
*/
|
|
25
|
+
export function getVersionData(): {
|
|
26
|
+
version: string;
|
|
27
|
+
commit: string;
|
|
28
|
+
} {
|
|
29
|
+
const parts: string[] = [];
|
|
30
|
+
|
|
31
|
+
/** Returns local version from `lerna.json` or `package.json` as `"0.28.2"` */
|
|
32
|
+
const localVersion = readCliPackageJson() || readVersionFromLernaJson();
|
|
33
|
+
if (localVersion) {
|
|
34
|
+
parts.push(`v${localVersion}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const {branch, commit} = readAndGetGitData();
|
|
38
|
+
|
|
39
|
+
// Add branch only if not present and not an ignore value
|
|
40
|
+
if (branch && !BRANCH_IGNORE.test(branch)) parts.push(branch);
|
|
41
|
+
|
|
42
|
+
// Add commit only if present. 7 characters to be consistent with Github
|
|
43
|
+
if (commit) {
|
|
44
|
+
const commitShort = commit.slice(0, 7);
|
|
45
|
+
// Don't add commit if it's already in the version string (dev versions)
|
|
46
|
+
if (!localVersion || !localVersion.includes(commitShort)) {
|
|
47
|
+
parts.push(commitShort);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
// Guard against empty parts array
|
|
53
|
+
version: parts.length > 0 ? parts.join("/") : "unknown",
|
|
54
|
+
commit,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Read version information from lerna.json */
|
|
59
|
+
function readVersionFromLernaJson(): string | undefined {
|
|
60
|
+
const filePath = findUpSync("lerna.json", {cwd: __dirname});
|
|
61
|
+
if (!filePath) return undefined;
|
|
62
|
+
|
|
63
|
+
const lernaJson = JSON.parse(fs.readFileSync(filePath, "utf8")) as VersionJson;
|
|
64
|
+
return lernaJson.version;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Read version information from package.json */
|
|
68
|
+
function readCliPackageJson(): string | undefined {
|
|
69
|
+
const filePath = findUpSync("package.json", {cwd: __dirname});
|
|
70
|
+
if (!filePath) return undefined;
|
|
71
|
+
|
|
72
|
+
const packageJson = JSON.parse(fs.readFileSync(filePath, "utf8")) as VersionJson;
|
|
73
|
+
return packageJson.version;
|
|
74
|
+
}
|