@chainsafe/lodestar 1.35.0-dev.c0078a16b5 → 1.35.0-dev.c1880f6940

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.
Files changed (214) hide show
  1. package/.git-data.json +1 -1
  2. package/bin/lodestar.js +3 -0
  3. package/bin/lodestar.ts +3 -0
  4. package/lib/applyPreset.d.ts.map +1 -0
  5. package/lib/cli.d.ts.map +1 -0
  6. package/lib/cmds/beacon/handler.d.ts +1 -1
  7. package/lib/cmds/beacon/handler.d.ts.map +1 -0
  8. package/lib/cmds/beacon/handler.js +1 -1
  9. package/lib/cmds/beacon/handler.js.map +1 -1
  10. package/lib/cmds/beacon/index.d.ts.map +1 -0
  11. package/lib/cmds/beacon/initBeaconState.d.ts.map +1 -0
  12. package/lib/cmds/beacon/initPeerIdAndEnr.d.ts.map +1 -0
  13. package/lib/cmds/beacon/options.d.ts.map +1 -0
  14. package/lib/cmds/beacon/paths.d.ts.map +1 -0
  15. package/lib/cmds/bootnode/handler.d.ts.map +1 -0
  16. package/lib/cmds/bootnode/index.d.ts.map +1 -0
  17. package/lib/cmds/bootnode/options.d.ts.map +1 -0
  18. package/lib/cmds/dev/files.d.ts.map +1 -0
  19. package/lib/cmds/dev/handler.d.ts.map +1 -0
  20. package/lib/cmds/dev/index.d.ts.map +1 -0
  21. package/lib/cmds/dev/options.d.ts.map +1 -0
  22. package/lib/cmds/index.d.ts.map +1 -0
  23. package/lib/cmds/lightclient/handler.d.ts.map +1 -0
  24. package/lib/cmds/lightclient/index.d.ts.map +1 -0
  25. package/lib/cmds/lightclient/options.d.ts.map +1 -0
  26. package/lib/cmds/validator/blsToExecutionChange.d.ts.map +1 -0
  27. package/lib/cmds/validator/handler.d.ts.map +1 -0
  28. package/lib/cmds/validator/handler.js +1 -1
  29. package/lib/cmds/validator/handler.js.map +1 -1
  30. package/lib/cmds/validator/import.d.ts.map +1 -0
  31. package/lib/cmds/validator/index.d.ts.map +1 -0
  32. package/lib/cmds/validator/keymanager/decryptKeystoreDefinitions.d.ts.map +1 -0
  33. package/lib/cmds/validator/keymanager/decryptKeystores/index.d.ts.map +1 -0
  34. package/lib/cmds/validator/keymanager/decryptKeystores/poolSize.d.ts.map +1 -0
  35. package/lib/cmds/validator/keymanager/decryptKeystores/threadPool.d.ts.map +1 -0
  36. package/lib/cmds/validator/keymanager/decryptKeystores/types.d.ts.map +1 -0
  37. package/lib/cmds/validator/keymanager/decryptKeystores/worker.d.ts.map +1 -0
  38. package/lib/cmds/validator/keymanager/impl.d.ts.map +1 -0
  39. package/lib/cmds/validator/keymanager/interface.d.ts.map +1 -0
  40. package/lib/cmds/validator/keymanager/keystoreCache.d.ts.map +1 -0
  41. package/lib/cmds/validator/keymanager/persistedKeys.d.ts.map +1 -0
  42. package/lib/cmds/validator/keymanager/server.d.ts.map +1 -0
  43. package/lib/cmds/validator/list.d.ts.map +1 -0
  44. package/lib/cmds/validator/options.d.ts.map +1 -0
  45. package/lib/cmds/validator/paths.d.ts.map +1 -0
  46. package/lib/cmds/validator/signers/importExternalKeystores.d.ts.map +1 -0
  47. package/lib/cmds/validator/signers/index.d.ts.map +1 -0
  48. package/lib/cmds/validator/signers/logSigners.d.ts.map +1 -0
  49. package/lib/cmds/validator/slashingProtection/export.d.ts.map +1 -0
  50. package/lib/cmds/validator/slashingProtection/import.d.ts.map +1 -0
  51. package/lib/cmds/validator/slashingProtection/index.d.ts.map +1 -0
  52. package/lib/cmds/validator/slashingProtection/options.d.ts.map +1 -0
  53. package/lib/cmds/validator/slashingProtection/utils.d.ts.map +1 -0
  54. package/lib/cmds/validator/slashingProtection/utils.js +1 -1
  55. package/lib/cmds/validator/slashingProtection/utils.js.map +1 -1
  56. package/lib/cmds/validator/voluntaryExit.d.ts.map +1 -0
  57. package/lib/config/beaconNodeOptions.d.ts.map +1 -0
  58. package/lib/config/beaconParams.d.ts.map +1 -0
  59. package/lib/config/index.d.ts.map +1 -0
  60. package/lib/config/peerId.d.ts.map +1 -0
  61. package/lib/config/types.d.ts.map +1 -0
  62. package/lib/index.d.ts.map +1 -0
  63. package/lib/migrations/index.d.ts.map +1 -0
  64. package/lib/networks/chiado.d.ts.map +1 -0
  65. package/lib/networks/dev.d.ts.map +1 -0
  66. package/lib/networks/ephemery.d.ts.map +1 -0
  67. package/lib/networks/gnosis.d.ts.map +1 -0
  68. package/lib/networks/holesky.d.ts.map +1 -0
  69. package/lib/networks/hoodi.d.ts.map +1 -0
  70. package/lib/networks/index.d.ts.map +1 -0
  71. package/lib/networks/mainnet.d.ts.map +1 -0
  72. package/lib/networks/sepolia.d.ts.map +1 -0
  73. package/lib/options/beaconNodeOptions/api.d.ts.map +1 -0
  74. package/lib/options/beaconNodeOptions/builder.d.ts.map +1 -0
  75. package/lib/options/beaconNodeOptions/chain.d.ts.map +1 -0
  76. package/lib/options/beaconNodeOptions/eth1.d.ts.map +1 -0
  77. package/lib/options/beaconNodeOptions/execution.d.ts.map +1 -0
  78. package/lib/options/beaconNodeOptions/index.d.ts.map +1 -0
  79. package/lib/options/beaconNodeOptions/metrics.d.ts.map +1 -0
  80. package/lib/options/beaconNodeOptions/monitoring.d.ts.map +1 -0
  81. package/lib/options/beaconNodeOptions/network.d.ts.map +1 -0
  82. package/lib/options/beaconNodeOptions/sync.d.ts.map +1 -0
  83. package/lib/options/globalOptions.d.ts.map +1 -0
  84. package/lib/options/index.d.ts.map +1 -0
  85. package/lib/options/logOptions.d.ts.map +1 -0
  86. package/lib/options/paramsOptions.d.ts.map +1 -0
  87. package/lib/paths/global.d.ts.map +1 -0
  88. package/lib/paths/rootDir.d.ts.map +1 -0
  89. package/lib/util/errors.d.ts.map +1 -0
  90. package/lib/util/ethers.d.ts.map +1 -0
  91. package/lib/util/feeRecipient.d.ts.map +1 -0
  92. package/lib/util/file.d.ts.map +1 -0
  93. package/lib/util/file.js.map +1 -1
  94. package/lib/util/format.d.ts.map +1 -0
  95. package/lib/util/fs.d.ts.map +1 -0
  96. package/lib/util/gitData/gitDataPath.d.ts.map +1 -0
  97. package/lib/util/gitData/index.d.ts.map +1 -0
  98. package/lib/util/gitData/writeGitData.d.ts.map +1 -0
  99. package/lib/util/index.d.ts.map +1 -0
  100. package/lib/util/jwt.d.ts.map +1 -0
  101. package/lib/util/lockfile.d.ts.map +1 -0
  102. package/lib/util/logger.d.ts.map +1 -0
  103. package/lib/util/object.d.ts.map +1 -0
  104. package/lib/util/passphrase.d.ts.map +1 -0
  105. package/lib/util/process.d.ts.map +1 -0
  106. package/lib/util/progress.d.ts.map +1 -0
  107. package/lib/util/proposerConfig.d.ts.map +1 -0
  108. package/lib/util/pruneOldFilesInDir.d.ts.map +1 -0
  109. package/lib/util/sleep.d.ts.map +1 -0
  110. package/lib/util/stripOffNewlines.d.ts.map +1 -0
  111. package/lib/util/types.d.ts.map +1 -0
  112. package/lib/util/version.d.ts.map +1 -0
  113. package/package.json +19 -18
  114. package/src/applyPreset.ts +91 -0
  115. package/src/cli.ts +56 -0
  116. package/src/cmds/beacon/handler.ts +267 -0
  117. package/src/cmds/beacon/index.ts +18 -0
  118. package/src/cmds/beacon/initBeaconState.ts +275 -0
  119. package/src/cmds/beacon/initPeerIdAndEnr.ts +199 -0
  120. package/src/cmds/beacon/options.ts +214 -0
  121. package/src/cmds/beacon/paths.ts +62 -0
  122. package/src/cmds/bootnode/handler.ts +203 -0
  123. package/src/cmds/bootnode/index.ts +13 -0
  124. package/src/cmds/bootnode/options.ts +109 -0
  125. package/src/cmds/dev/files.ts +52 -0
  126. package/src/cmds/dev/handler.ts +86 -0
  127. package/src/cmds/dev/index.ts +18 -0
  128. package/src/cmds/dev/options.ts +110 -0
  129. package/src/cmds/index.ts +15 -0
  130. package/src/cmds/lightclient/handler.ts +36 -0
  131. package/src/cmds/lightclient/index.ts +18 -0
  132. package/src/cmds/lightclient/options.ts +21 -0
  133. package/src/cmds/validator/blsToExecutionChange.ts +91 -0
  134. package/src/cmds/validator/handler.ts +300 -0
  135. package/src/cmds/validator/import.ts +111 -0
  136. package/src/cmds/validator/index.ts +28 -0
  137. package/src/cmds/validator/keymanager/decryptKeystoreDefinitions.ts +189 -0
  138. package/src/cmds/validator/keymanager/decryptKeystores/index.ts +1 -0
  139. package/src/cmds/validator/keymanager/decryptKeystores/poolSize.ts +16 -0
  140. package/src/cmds/validator/keymanager/decryptKeystores/threadPool.ts +75 -0
  141. package/src/cmds/validator/keymanager/decryptKeystores/types.ts +12 -0
  142. package/src/cmds/validator/keymanager/decryptKeystores/worker.ts +24 -0
  143. package/src/cmds/validator/keymanager/impl.ts +425 -0
  144. package/src/cmds/validator/keymanager/interface.ts +35 -0
  145. package/src/cmds/validator/keymanager/keystoreCache.ts +91 -0
  146. package/src/cmds/validator/keymanager/persistedKeys.ts +268 -0
  147. package/src/cmds/validator/keymanager/server.ts +86 -0
  148. package/src/cmds/validator/list.ts +35 -0
  149. package/src/cmds/validator/options.ts +461 -0
  150. package/src/cmds/validator/paths.ts +95 -0
  151. package/src/cmds/validator/signers/importExternalKeystores.ts +69 -0
  152. package/src/cmds/validator/signers/index.ts +176 -0
  153. package/src/cmds/validator/signers/logSigners.ts +81 -0
  154. package/src/cmds/validator/slashingProtection/export.ts +110 -0
  155. package/src/cmds/validator/slashingProtection/import.ts +70 -0
  156. package/src/cmds/validator/slashingProtection/index.ts +12 -0
  157. package/src/cmds/validator/slashingProtection/options.ts +15 -0
  158. package/src/cmds/validator/slashingProtection/utils.ts +56 -0
  159. package/src/cmds/validator/voluntaryExit.ts +232 -0
  160. package/src/config/beaconNodeOptions.ts +68 -0
  161. package/src/config/beaconParams.ts +87 -0
  162. package/src/config/index.ts +3 -0
  163. package/src/config/peerId.ts +50 -0
  164. package/src/config/types.ts +3 -0
  165. package/src/index.ts +28 -0
  166. package/src/migrations/index.ts +0 -0
  167. package/src/networks/chiado.ts +20 -0
  168. package/src/networks/dev.ts +27 -0
  169. package/src/networks/ephemery.ts +9 -0
  170. package/src/networks/gnosis.ts +18 -0
  171. package/src/networks/holesky.ts +17 -0
  172. package/src/networks/hoodi.ts +16 -0
  173. package/src/networks/index.ts +236 -0
  174. package/src/networks/mainnet.ts +34 -0
  175. package/src/networks/sepolia.ts +17 -0
  176. package/src/options/beaconNodeOptions/api.ts +110 -0
  177. package/src/options/beaconNodeOptions/builder.ts +63 -0
  178. package/src/options/beaconNodeOptions/chain.ts +326 -0
  179. package/src/options/beaconNodeOptions/eth1.ts +95 -0
  180. package/src/options/beaconNodeOptions/execution.ts +92 -0
  181. package/src/options/beaconNodeOptions/index.ts +50 -0
  182. package/src/options/beaconNodeOptions/metrics.ts +39 -0
  183. package/src/options/beaconNodeOptions/monitoring.ts +61 -0
  184. package/src/options/beaconNodeOptions/network.ts +401 -0
  185. package/src/options/beaconNodeOptions/sync.ts +65 -0
  186. package/src/options/globalOptions.ts +72 -0
  187. package/src/options/index.ts +3 -0
  188. package/src/options/logOptions.ts +70 -0
  189. package/src/options/paramsOptions.ts +72 -0
  190. package/src/paths/global.ts +24 -0
  191. package/src/paths/rootDir.ts +11 -0
  192. package/src/util/errors.ts +20 -0
  193. package/src/util/ethers.ts +44 -0
  194. package/src/util/feeRecipient.ts +6 -0
  195. package/src/util/file.ts +167 -0
  196. package/src/util/format.ts +76 -0
  197. package/src/util/fs.ts +59 -0
  198. package/src/util/gitData/gitDataPath.ts +48 -0
  199. package/src/util/gitData/index.ts +70 -0
  200. package/src/util/gitData/writeGitData.ts +10 -0
  201. package/src/util/index.ts +17 -0
  202. package/src/util/jwt.ts +10 -0
  203. package/src/util/lockfile.ts +45 -0
  204. package/src/util/logger.ts +105 -0
  205. package/src/util/object.ts +15 -0
  206. package/src/util/passphrase.ts +25 -0
  207. package/src/util/process.ts +25 -0
  208. package/src/util/progress.ts +58 -0
  209. package/src/util/proposerConfig.ts +136 -0
  210. package/src/util/pruneOldFilesInDir.ts +27 -0
  211. package/src/util/sleep.ts +3 -0
  212. package/src/util/stripOffNewlines.ts +6 -0
  213. package/src/util/types.ts +8 -0
  214. 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,3 @@
1
+ export function sleep(ms: number): Promise<void> {
2
+ return new Promise((resolve) => setTimeout(resolve, ms));
3
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Remove trailing new lines '\n' or '\r' if any
3
+ */
4
+ export function stripOffNewlines(s: string): string {
5
+ return s.replace(/[\n\r]+$/g, "");
6
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Typed `Object.keys(o: T)` function, returning `(keyof T)[]`
3
+ */
4
+
5
+ // biome-ignore lint/suspicious/noExplicitAny: We need to use `any` type here
6
+ export function ObjectKeys<T extends {[key: string]: any}>(o: T): (keyof T)[] {
7
+ return Object.keys(o);
8
+ }
@@ -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
+ }