@chainsafe/lodestar 1.35.0-dev.e18102ed8c → 1.35.0-dev.f2a741bbe4

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 (233) 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/applyPreset.js +1 -1
  6. package/lib/applyPreset.js.map +1 -1
  7. package/lib/cli.d.ts.map +1 -0
  8. package/lib/cmds/beacon/handler.d.ts +1 -1
  9. package/lib/cmds/beacon/handler.d.ts.map +1 -0
  10. package/lib/cmds/beacon/handler.js +1 -1
  11. package/lib/cmds/beacon/handler.js.map +1 -1
  12. package/lib/cmds/beacon/index.d.ts.map +1 -0
  13. package/lib/cmds/beacon/initBeaconState.d.ts.map +1 -0
  14. package/lib/cmds/beacon/initPeerIdAndEnr.d.ts.map +1 -0
  15. package/lib/cmds/beacon/options.d.ts.map +1 -0
  16. package/lib/cmds/beacon/paths.d.ts.map +1 -0
  17. package/lib/cmds/bootnode/handler.d.ts.map +1 -0
  18. package/lib/cmds/bootnode/index.d.ts.map +1 -0
  19. package/lib/cmds/bootnode/options.d.ts.map +1 -0
  20. package/lib/cmds/dev/files.d.ts.map +1 -0
  21. package/lib/cmds/dev/handler.d.ts.map +1 -0
  22. package/lib/cmds/dev/index.d.ts.map +1 -0
  23. package/lib/cmds/dev/options.d.ts.map +1 -0
  24. package/lib/cmds/index.d.ts.map +1 -0
  25. package/lib/cmds/lightclient/handler.d.ts.map +1 -0
  26. package/lib/cmds/lightclient/index.d.ts.map +1 -0
  27. package/lib/cmds/lightclient/options.d.ts.map +1 -0
  28. package/lib/cmds/validator/blsToExecutionChange.d.ts.map +1 -0
  29. package/lib/cmds/validator/handler.d.ts.map +1 -0
  30. package/lib/cmds/validator/handler.js +1 -1
  31. package/lib/cmds/validator/handler.js.map +1 -1
  32. package/lib/cmds/validator/import.d.ts.map +1 -0
  33. package/lib/cmds/validator/index.d.ts.map +1 -0
  34. package/lib/cmds/validator/keymanager/decryptKeystoreDefinitions.d.ts.map +1 -0
  35. package/lib/cmds/validator/keymanager/decryptKeystores/index.d.ts.map +1 -0
  36. package/lib/cmds/validator/keymanager/decryptKeystores/poolSize.d.ts.map +1 -0
  37. package/lib/cmds/validator/keymanager/decryptKeystores/threadPool.d.ts.map +1 -0
  38. package/lib/cmds/validator/keymanager/decryptKeystores/types.d.ts.map +1 -0
  39. package/lib/cmds/validator/keymanager/decryptKeystores/worker.d.ts.map +1 -0
  40. package/lib/cmds/validator/keymanager/impl.d.ts.map +1 -0
  41. package/lib/cmds/validator/keymanager/interface.d.ts.map +1 -0
  42. package/lib/cmds/validator/keymanager/keystoreCache.d.ts.map +1 -0
  43. package/lib/cmds/validator/keymanager/persistedKeys.d.ts.map +1 -0
  44. package/lib/cmds/validator/keymanager/server.d.ts.map +1 -0
  45. package/lib/cmds/validator/list.d.ts.map +1 -0
  46. package/lib/cmds/validator/options.d.ts.map +1 -0
  47. package/lib/cmds/validator/options.js +5 -3
  48. package/lib/cmds/validator/options.js.map +1 -1
  49. package/lib/cmds/validator/paths.d.ts.map +1 -0
  50. package/lib/cmds/validator/signers/importExternalKeystores.d.ts.map +1 -0
  51. package/lib/cmds/validator/signers/index.d.ts.map +1 -0
  52. package/lib/cmds/validator/signers/logSigners.d.ts.map +1 -0
  53. package/lib/cmds/validator/slashingProtection/export.d.ts.map +1 -0
  54. package/lib/cmds/validator/slashingProtection/import.d.ts.map +1 -0
  55. package/lib/cmds/validator/slashingProtection/index.d.ts.map +1 -0
  56. package/lib/cmds/validator/slashingProtection/options.d.ts.map +1 -0
  57. package/lib/cmds/validator/slashingProtection/utils.d.ts.map +1 -0
  58. package/lib/cmds/validator/slashingProtection/utils.js +1 -1
  59. package/lib/cmds/validator/slashingProtection/utils.js.map +1 -1
  60. package/lib/cmds/validator/voluntaryExit.d.ts.map +1 -0
  61. package/lib/config/beaconNodeOptions.d.ts.map +1 -0
  62. package/lib/config/beaconParams.d.ts.map +1 -0
  63. package/lib/config/index.d.ts.map +1 -0
  64. package/lib/config/peerId.d.ts.map +1 -0
  65. package/lib/config/types.d.ts.map +1 -0
  66. package/lib/index.d.ts.map +1 -0
  67. package/lib/migrations/index.d.ts.map +1 -0
  68. package/lib/networks/chiado.d.ts.map +1 -0
  69. package/lib/networks/dev.d.ts.map +1 -0
  70. package/lib/networks/ephemery.d.ts.map +1 -0
  71. package/lib/networks/gnosis.d.ts.map +1 -0
  72. package/lib/networks/holesky.d.ts.map +1 -0
  73. package/lib/networks/hoodi.d.ts.map +1 -0
  74. package/lib/networks/index.d.ts.map +1 -0
  75. package/lib/networks/mainnet.d.ts.map +1 -0
  76. package/lib/networks/sepolia.d.ts.map +1 -0
  77. package/lib/options/beaconNodeOptions/api.d.ts +6 -0
  78. package/lib/options/beaconNodeOptions/api.d.ts.map +1 -0
  79. package/lib/options/beaconNodeOptions/api.js +14 -6
  80. package/lib/options/beaconNodeOptions/api.js.map +1 -1
  81. package/lib/options/beaconNodeOptions/builder.d.ts.map +1 -0
  82. package/lib/options/beaconNodeOptions/chain.d.ts.map +1 -0
  83. package/lib/options/beaconNodeOptions/eth1.d.ts.map +1 -0
  84. package/lib/options/beaconNodeOptions/execution.d.ts.map +1 -0
  85. package/lib/options/beaconNodeOptions/index.d.ts.map +1 -0
  86. package/lib/options/beaconNodeOptions/metrics.d.ts.map +1 -0
  87. package/lib/options/beaconNodeOptions/monitoring.d.ts.map +1 -0
  88. package/lib/options/beaconNodeOptions/network.d.ts.map +1 -0
  89. package/lib/options/beaconNodeOptions/sync.d.ts.map +1 -0
  90. package/lib/options/globalOptions.d.ts.map +1 -0
  91. package/lib/options/index.d.ts.map +1 -0
  92. package/lib/options/logOptions.d.ts.map +1 -0
  93. package/lib/options/paramsOptions.d.ts.map +1 -0
  94. package/lib/paths/global.d.ts.map +1 -0
  95. package/lib/paths/rootDir.d.ts.map +1 -0
  96. package/lib/util/errors.d.ts.map +1 -0
  97. package/lib/util/ethers.d.ts.map +1 -0
  98. package/lib/util/feeRecipient.d.ts.map +1 -0
  99. package/lib/util/file.d.ts.map +1 -0
  100. package/lib/util/file.js.map +1 -1
  101. package/lib/util/format.d.ts.map +1 -0
  102. package/lib/util/fs.d.ts.map +1 -0
  103. package/lib/util/gitData/gitDataPath.d.ts.map +1 -0
  104. package/lib/util/gitData/index.d.ts.map +1 -0
  105. package/lib/util/gitData/writeGitData.d.ts.map +1 -0
  106. package/lib/util/hasher_bun.d.ts +3 -0
  107. package/lib/util/hasher_bun.d.ts.map +1 -0
  108. package/lib/util/hasher_bun.js +118 -0
  109. package/lib/util/hasher_bun.js.map +1 -0
  110. package/lib/util/hasher_nodejs.d.ts +3 -0
  111. package/lib/util/hasher_nodejs.d.ts.map +1 -0
  112. package/lib/util/hasher_nodejs.js +3 -0
  113. package/lib/util/hasher_nodejs.js.map +1 -0
  114. package/lib/util/index.d.ts.map +1 -0
  115. package/lib/util/jwt.d.ts.map +1 -0
  116. package/lib/util/lockfile.d.ts.map +1 -0
  117. package/lib/util/logger.d.ts.map +1 -0
  118. package/lib/util/logger.js +1 -1
  119. package/lib/util/logger.js.map +1 -1
  120. package/lib/util/object.d.ts.map +1 -0
  121. package/lib/util/passphrase.d.ts.map +1 -0
  122. package/lib/util/process.d.ts.map +1 -0
  123. package/lib/util/progress.d.ts.map +1 -0
  124. package/lib/util/proposerConfig.d.ts.map +1 -0
  125. package/lib/util/pruneOldFilesInDir.d.ts.map +1 -0
  126. package/lib/util/sleep.d.ts.map +1 -0
  127. package/lib/util/stripOffNewlines.d.ts.map +1 -0
  128. package/lib/util/types.d.ts.map +1 -0
  129. package/lib/util/version.d.ts.map +1 -0
  130. package/package.json +32 -19
  131. package/src/applyPreset.ts +92 -0
  132. package/src/cli.ts +56 -0
  133. package/src/cmds/beacon/handler.ts +267 -0
  134. package/src/cmds/beacon/index.ts +18 -0
  135. package/src/cmds/beacon/initBeaconState.ts +275 -0
  136. package/src/cmds/beacon/initPeerIdAndEnr.ts +199 -0
  137. package/src/cmds/beacon/options.ts +214 -0
  138. package/src/cmds/beacon/paths.ts +62 -0
  139. package/src/cmds/bootnode/handler.ts +203 -0
  140. package/src/cmds/bootnode/index.ts +13 -0
  141. package/src/cmds/bootnode/options.ts +109 -0
  142. package/src/cmds/dev/files.ts +52 -0
  143. package/src/cmds/dev/handler.ts +86 -0
  144. package/src/cmds/dev/index.ts +18 -0
  145. package/src/cmds/dev/options.ts +110 -0
  146. package/src/cmds/index.ts +15 -0
  147. package/src/cmds/lightclient/handler.ts +36 -0
  148. package/src/cmds/lightclient/index.ts +18 -0
  149. package/src/cmds/lightclient/options.ts +21 -0
  150. package/src/cmds/validator/blsToExecutionChange.ts +91 -0
  151. package/src/cmds/validator/handler.ts +300 -0
  152. package/src/cmds/validator/import.ts +111 -0
  153. package/src/cmds/validator/index.ts +28 -0
  154. package/src/cmds/validator/keymanager/decryptKeystoreDefinitions.ts +189 -0
  155. package/src/cmds/validator/keymanager/decryptKeystores/index.ts +1 -0
  156. package/src/cmds/validator/keymanager/decryptKeystores/poolSize.ts +16 -0
  157. package/src/cmds/validator/keymanager/decryptKeystores/threadPool.ts +75 -0
  158. package/src/cmds/validator/keymanager/decryptKeystores/types.ts +12 -0
  159. package/src/cmds/validator/keymanager/decryptKeystores/worker.ts +24 -0
  160. package/src/cmds/validator/keymanager/impl.ts +425 -0
  161. package/src/cmds/validator/keymanager/interface.ts +35 -0
  162. package/src/cmds/validator/keymanager/keystoreCache.ts +91 -0
  163. package/src/cmds/validator/keymanager/persistedKeys.ts +268 -0
  164. package/src/cmds/validator/keymanager/server.ts +86 -0
  165. package/src/cmds/validator/list.ts +35 -0
  166. package/src/cmds/validator/options.ts +463 -0
  167. package/src/cmds/validator/paths.ts +95 -0
  168. package/src/cmds/validator/signers/importExternalKeystores.ts +69 -0
  169. package/src/cmds/validator/signers/index.ts +176 -0
  170. package/src/cmds/validator/signers/logSigners.ts +81 -0
  171. package/src/cmds/validator/slashingProtection/export.ts +110 -0
  172. package/src/cmds/validator/slashingProtection/import.ts +70 -0
  173. package/src/cmds/validator/slashingProtection/index.ts +12 -0
  174. package/src/cmds/validator/slashingProtection/options.ts +15 -0
  175. package/src/cmds/validator/slashingProtection/utils.ts +56 -0
  176. package/src/cmds/validator/voluntaryExit.ts +232 -0
  177. package/src/config/beaconNodeOptions.ts +68 -0
  178. package/src/config/beaconParams.ts +87 -0
  179. package/src/config/index.ts +3 -0
  180. package/src/config/peerId.ts +50 -0
  181. package/src/config/types.ts +3 -0
  182. package/src/index.ts +28 -0
  183. package/src/migrations/index.ts +0 -0
  184. package/src/networks/chiado.ts +20 -0
  185. package/src/networks/dev.ts +27 -0
  186. package/src/networks/ephemery.ts +9 -0
  187. package/src/networks/gnosis.ts +18 -0
  188. package/src/networks/holesky.ts +17 -0
  189. package/src/networks/hoodi.ts +16 -0
  190. package/src/networks/index.ts +236 -0
  191. package/src/networks/mainnet.ts +34 -0
  192. package/src/networks/sepolia.ts +17 -0
  193. package/src/options/beaconNodeOptions/api.ts +120 -0
  194. package/src/options/beaconNodeOptions/builder.ts +63 -0
  195. package/src/options/beaconNodeOptions/chain.ts +326 -0
  196. package/src/options/beaconNodeOptions/eth1.ts +95 -0
  197. package/src/options/beaconNodeOptions/execution.ts +92 -0
  198. package/src/options/beaconNodeOptions/index.ts +50 -0
  199. package/src/options/beaconNodeOptions/metrics.ts +39 -0
  200. package/src/options/beaconNodeOptions/monitoring.ts +61 -0
  201. package/src/options/beaconNodeOptions/network.ts +401 -0
  202. package/src/options/beaconNodeOptions/sync.ts +65 -0
  203. package/src/options/globalOptions.ts +72 -0
  204. package/src/options/index.ts +3 -0
  205. package/src/options/logOptions.ts +70 -0
  206. package/src/options/paramsOptions.ts +72 -0
  207. package/src/paths/global.ts +24 -0
  208. package/src/paths/rootDir.ts +11 -0
  209. package/src/util/errors.ts +20 -0
  210. package/src/util/ethers.ts +44 -0
  211. package/src/util/feeRecipient.ts +6 -0
  212. package/src/util/file.ts +167 -0
  213. package/src/util/format.ts +76 -0
  214. package/src/util/fs.ts +59 -0
  215. package/src/util/gitData/gitDataPath.ts +48 -0
  216. package/src/util/gitData/index.ts +70 -0
  217. package/src/util/gitData/writeGitData.ts +10 -0
  218. package/src/util/hasher_bun.ts +133 -0
  219. package/src/util/hasher_nodejs.ts +3 -0
  220. package/src/util/index.ts +17 -0
  221. package/src/util/jwt.ts +10 -0
  222. package/src/util/lockfile.ts +45 -0
  223. package/src/util/logger.ts +105 -0
  224. package/src/util/object.ts +15 -0
  225. package/src/util/passphrase.ts +25 -0
  226. package/src/util/process.ts +25 -0
  227. package/src/util/progress.ts +58 -0
  228. package/src/util/proposerConfig.ts +136 -0
  229. package/src/util/pruneOldFilesInDir.ts +27 -0
  230. package/src/util/sleep.ts +3 -0
  231. package/src/util/stripOffNewlines.ts +6 -0
  232. package/src/util/types.ts +8 -0
  233. package/src/util/version.ts +74 -0
@@ -0,0 +1,268 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import {Keystore} from "@chainsafe/bls-keystore";
4
+ import {DeletionStatus, ImportStatus, PubkeyHex, SignerDefinition} from "@lodestar/api/keymanager";
5
+ import {ProposerConfig} from "@lodestar/validator";
6
+ import {
7
+ getPubkeyHexFromKeystore,
8
+ readPassphraseFile,
9
+ readProposerConfigDir,
10
+ rmdirSyncMaybe,
11
+ unlinkSyncMaybe,
12
+ writeFile600Perm,
13
+ } from "../../../util/index.js";
14
+ import {lockFilepath} from "../../../util/lockfile.js";
15
+ import {IPersistedKeysBackend, LocalKeystoreDefinition} from "./interface.js";
16
+
17
+ export {ImportStatus, DeletionStatus};
18
+
19
+ type PathArgs = {
20
+ keystoresDir: string;
21
+ secretsDir: string;
22
+ remoteKeysDir: string;
23
+ proposerDir: string;
24
+ };
25
+
26
+ /**
27
+ * Class to unify read+write of keystores and remoteKeys from disk.
28
+ * Consumers of this class include:
29
+ * - validator cmd: Read all keystores + lock them, and read all remote keys
30
+ * - import cmd: Write keystores
31
+ * - list cmd: Read all keystores
32
+ * - keymanager importKeystores route: Write keystores + lock them
33
+ * - keymanager importRemoteKeys route: Write remote keys
34
+ *
35
+ * This logic ensures no inconsistencies between all methods of read + write.
36
+ * It also ensures that keystores lockfiles are consistent and checked in all code paths.
37
+ *
38
+ * NOTES:
39
+ * - Keystores imported via keymanager API behave the same and import cmd end result.
40
+ * - Logic to scan an external dir for keystores is the same for import cmd and validator cmd.
41
+ * - lockfile locks are not explicitly released. The underlying library handles that automatically
42
+ * - Imported remote key definitions are stored in a separate directory from imported keystores
43
+ */
44
+ export class PersistedKeysBackend implements IPersistedKeysBackend {
45
+ constructor(private readonly paths: PathArgs) {}
46
+
47
+ writeProposerConfig(pubkeyHex: PubkeyHex, proposerConfig: ProposerConfig | null): void {
48
+ if (!fs.existsSync(this.paths.proposerDir)) {
49
+ // create directory
50
+ fs.mkdirSync(this.paths.proposerDir);
51
+ }
52
+
53
+ if (proposerConfig !== null) {
54
+ // if proposerConfig is not empty write or update the json to file
55
+ const {proposerDirPath} = this.getValidatorPaths(pubkeyHex);
56
+ writeFile600Perm(proposerDirPath, JSON.stringify(proposerConfig));
57
+ } else {
58
+ this.deleteProposerConfig(pubkeyHex);
59
+ }
60
+ }
61
+
62
+ deleteProposerConfig(pubkeyHex: PubkeyHex): void {
63
+ if (fs.existsSync(this.paths.proposerDir)) {
64
+ const {proposerDirPath} = this.getValidatorPaths(pubkeyHex);
65
+ unlinkSyncMaybe(proposerDirPath);
66
+ }
67
+ }
68
+
69
+ readProposerConfigs(): {[index: string]: ProposerConfig} {
70
+ if (!fs.existsSync(this.paths.proposerDir)) {
71
+ return {};
72
+ }
73
+ const proposerConfigs = {};
74
+
75
+ for (const pubkey of fs.readdirSync(this.paths.proposerDir)) {
76
+ Object.assign(proposerConfigs, {[pubkey]: readProposerConfigDir(this.paths.proposerDir, pubkey)});
77
+ }
78
+ return proposerConfigs;
79
+ }
80
+
81
+ deleteProposerConfigs(): void {
82
+ for (const pubkey of fs.readdirSync(this.paths.proposerDir)) {
83
+ this.deleteProposerConfig(pubkey);
84
+ }
85
+ }
86
+
87
+ readAllKeystores(): LocalKeystoreDefinition[] {
88
+ const {keystoresDir} = this.paths;
89
+
90
+ if (!fs.existsSync(keystoresDir)) {
91
+ return [];
92
+ }
93
+
94
+ const keystoreDefinitions: LocalKeystoreDefinition[] = [];
95
+
96
+ for (const pubkey of fs.readdirSync(keystoresDir)) {
97
+ const {dirpath, keystoreFilepath, passphraseFilepath} = this.getValidatorPaths(pubkey);
98
+
99
+ if (fs.statSync(dirpath).isDirectory()) {
100
+ keystoreDefinitions.push({
101
+ keystorePath: keystoreFilepath,
102
+ password: readPassphraseFile(passphraseFilepath),
103
+ });
104
+ }
105
+ }
106
+
107
+ return keystoreDefinitions;
108
+ }
109
+
110
+ writeKeystore({
111
+ keystoreStr,
112
+ password,
113
+ lockBeforeWrite,
114
+ persistIfDuplicate,
115
+ }: {
116
+ keystoreStr: string;
117
+ password: string;
118
+ lockBeforeWrite: boolean;
119
+ persistIfDuplicate: boolean;
120
+ }): boolean {
121
+ // Validate Keystore JSON + pubkey format.
122
+ // Note: while this is currently redundant, it's free to check that format is correct before writting
123
+ const keystore = Keystore.parse(keystoreStr);
124
+ const pubkeyHex = getPubkeyHexFromKeystore(keystore);
125
+
126
+ const {dirpath, keystoreFilepath, passphraseFilepath} = this.getValidatorPaths(pubkeyHex);
127
+
128
+ // Check if duplicate first.
129
+ // TODO: Check that the content is actually equal. But not naively, the JSON could be formated differently
130
+ if (!persistIfDuplicate && fs.existsSync(keystoreFilepath)) {
131
+ return false;
132
+ }
133
+
134
+ // Make dirs before creating the lock
135
+ fs.mkdirSync(this.paths.secretsDir, {recursive: true});
136
+ fs.mkdirSync(dirpath, {recursive: true});
137
+
138
+ if (lockBeforeWrite) {
139
+ // Lock before writing keystore
140
+ lockFilepath(keystoreFilepath);
141
+ }
142
+
143
+ writeFile600Perm(keystoreFilepath, keystoreStr);
144
+ writeFile600Perm(passphraseFilepath, password);
145
+
146
+ return true;
147
+ }
148
+
149
+ /** Returns true if some component was actually deleted */
150
+ deleteKeystore(pubkey: PubkeyHex): boolean {
151
+ const {dirpath, keystoreFilepath, passphraseFilepath} = this.getValidatorPaths(pubkey);
152
+
153
+ // Attempt to delete everything, retaining each status
154
+ const deletedKeystore = unlinkSyncMaybe(keystoreFilepath);
155
+ const deletedPassphrase = unlinkSyncMaybe(passphraseFilepath);
156
+ const deletedDir = rmdirSyncMaybe(dirpath);
157
+
158
+ // TODO: Unlock keystore .lock
159
+ // Note: not really necessary since current lockfile lib does that automatically on process exit
160
+
161
+ return deletedKeystore || deletedPassphrase || deletedDir;
162
+ }
163
+
164
+ readAllRemoteKeys(): SignerDefinition[] {
165
+ const signerDefinitions: SignerDefinition[] = [];
166
+
167
+ if (!fs.existsSync(this.paths.remoteKeysDir)) {
168
+ return [];
169
+ }
170
+
171
+ for (const pubkey of fs.readdirSync(this.paths.remoteKeysDir)) {
172
+ const {definitionFilepath} = this.getDefinitionPaths(pubkey);
173
+ signerDefinitions.push(readRemoteSignerDefinition(definitionFilepath));
174
+ }
175
+
176
+ return signerDefinitions;
177
+ }
178
+
179
+ writeRemoteKey({
180
+ pubkey,
181
+ url,
182
+ persistIfDuplicate,
183
+ }: {
184
+ pubkey: PubkeyHex;
185
+ url: string;
186
+ persistIfDuplicate: boolean;
187
+ }): boolean {
188
+ const {definitionFilepath} = this.getDefinitionPaths(pubkey);
189
+
190
+ // Check if duplicate first.
191
+ // TODO: Check that the content is actually equal. But not naively, the JSON could be formated differently
192
+ if (!persistIfDuplicate && fs.existsSync(definitionFilepath)) {
193
+ return false;
194
+ }
195
+
196
+ fs.mkdirSync(path.dirname(definitionFilepath), {recursive: true});
197
+ writeRemoteSignerDefinition(definitionFilepath, {
198
+ pubkey,
199
+ url,
200
+ readonly: false,
201
+ });
202
+
203
+ return true;
204
+ }
205
+
206
+ /** Returns true if it was actually deleted */
207
+ deleteRemoteKey(pubkey: PubkeyHex): boolean {
208
+ const {definitionFilepath} = this.getDefinitionPaths(pubkey);
209
+
210
+ // Attempt to delete everything, retaining each status
211
+ return unlinkSyncMaybe(definitionFilepath);
212
+ }
213
+
214
+ private getDefinitionPaths(pubkey: PubkeyHex): {definitionFilepath: string} {
215
+ // TODO: Ensure correct formating 0x prefixed
216
+
217
+ return {
218
+ definitionFilepath: path.join(this.paths.remoteKeysDir, pubkey),
219
+ };
220
+ }
221
+
222
+ private getValidatorPaths(pubkey: PubkeyHex): {
223
+ dirpath: string;
224
+ keystoreFilepath: string;
225
+ passphraseFilepath: string;
226
+ proposerDirPath: string;
227
+ } {
228
+ // TODO: Ensure correct formating 0x prefixed
229
+
230
+ const dirpath = path.join(this.paths.keystoresDir, pubkey);
231
+
232
+ return {
233
+ dirpath,
234
+ keystoreFilepath: path.join(dirpath, "voting-keystore.json"),
235
+ passphraseFilepath: path.join(this.paths.secretsDir, pubkey),
236
+ proposerDirPath: path.join(this.paths.proposerDir, pubkey),
237
+ };
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Validate SignerDefinition from un-trusted disk file.
243
+ * Performs type validation and re-maps only expected properties.
244
+ */
245
+ export function readRemoteSignerDefinition(filepath: string): SignerDefinition {
246
+ const remoteSignerStr = fs.readFileSync(filepath, "utf8");
247
+ const remoteSignerJson = JSON.parse(remoteSignerStr) as SignerDefinition;
248
+ if (typeof remoteSignerJson.pubkey !== "string") throw Error(`invalid SignerDefinition.pubkey ${filepath}`);
249
+ if (typeof remoteSignerJson.url !== "string") throw Error(`invalid SignerDefinition.url ${filepath}`);
250
+ return {
251
+ pubkey: remoteSignerJson.pubkey,
252
+ url: remoteSignerJson.url,
253
+ readonly: false,
254
+ };
255
+ }
256
+
257
+ /**
258
+ * Re-map all properties to ensure they are defined.
259
+ * To just write `remoteSigner` is not safe since it may contain extra properties too.
260
+ */
261
+ export function writeRemoteSignerDefinition(filepath: string, remoteSigner: SignerDefinition): void {
262
+ const remoteSignerJson: SignerDefinition = {
263
+ pubkey: remoteSigner.pubkey,
264
+ url: remoteSigner.url,
265
+ readonly: false,
266
+ };
267
+ writeFile600Perm(filepath, JSON.stringify(remoteSignerJson));
268
+ }
@@ -0,0 +1,86 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import {KeymanagerApiMethods, registerRoutes} from "@lodestar/api/keymanager/server";
5
+ import {RestApiServer, RestApiServerModules, RestApiServerOpts} from "@lodestar/beacon-node";
6
+ import {ChainForkConfig} from "@lodestar/config";
7
+ import {toHex} from "@lodestar/utils";
8
+ import {writeFile600Perm} from "../../../util/index.js";
9
+
10
+ export type KeymanagerRestApiServerOpts = RestApiServerOpts & {
11
+ isAuthEnabled: boolean;
12
+ tokenDir?: string;
13
+ // Takes precedence over `tokenDir`
14
+ tokenFile?: string;
15
+ };
16
+
17
+ export const keymanagerRestApiServerOptsDefault: KeymanagerRestApiServerOpts = {
18
+ address: "127.0.0.1",
19
+ port: 5062,
20
+ cors: "*",
21
+ isAuthEnabled: true,
22
+ // Slashing protection DB has been reported to be 3MB https://github.com/ChainSafe/lodestar/issues/4530
23
+ bodyLimit: 20 * 1024 * 1024, // 20MB
24
+ stacktraces: false,
25
+ };
26
+
27
+ export type KeymanagerRestApiServerModules = RestApiServerModules & {
28
+ config: ChainForkConfig;
29
+ api: KeymanagerApiMethods;
30
+ };
31
+
32
+ export const apiTokenFileName = "api-token.txt";
33
+
34
+ export class KeymanagerRestApiServer extends RestApiServer {
35
+ private readonly apiTokenPath: string;
36
+ private readonly isAuthEnabled: boolean;
37
+
38
+ constructor(optsArg: Partial<KeymanagerRestApiServerOpts>, modules: KeymanagerRestApiServerModules) {
39
+ // Apply opts defaults
40
+ const opts = {
41
+ ...keymanagerRestApiServerOptsDefault,
42
+ // optsArg is a Partial type, any of its properties can be undefined. If port is set to undefined,
43
+ // it overrides the default port value in restApiOptionsDefault to be undefined.
44
+ ...Object.fromEntries(Object.entries(optsArg).filter(([_, v]) => v != null)),
45
+ };
46
+
47
+ const apiTokenPath = opts.tokenFile
48
+ ? path.resolve(opts.tokenFile)
49
+ : path.join(opts.tokenDir ?? ".", apiTokenFileName);
50
+ let bearerToken: string | undefined;
51
+
52
+ if (opts.isAuthEnabled) {
53
+ // Generate a new token if token file does not exist or file do exist, but is empty
54
+ bearerToken = readFileIfExists(apiTokenPath) ?? `api-token-${toHex(crypto.randomBytes(32))}`;
55
+ writeFile600Perm(apiTokenPath, bearerToken, {encoding: "utf8"});
56
+ }
57
+
58
+ super({...opts, bearerToken}, modules);
59
+
60
+ // Instantiate and register the keymanager routes
61
+ registerRoutes(this.server, modules.config, modules.api);
62
+
63
+ this.apiTokenPath = apiTokenPath;
64
+ this.isAuthEnabled = opts.isAuthEnabled;
65
+ }
66
+
67
+ async listen(): Promise<void> {
68
+ await super.listen();
69
+
70
+ if (this.isAuthEnabled) {
71
+ this.logger.info(`REST api server keymanager bearer access token located at:\n\n${this.apiTokenPath}\n`);
72
+ } else {
73
+ this.logger.warn("REST api server keymanager started without authentication");
74
+ }
75
+ }
76
+ }
77
+
78
+ function readFileIfExists(filepath: string): string | null {
79
+ try {
80
+ return fs.readFileSync(filepath, "utf8").trim();
81
+ } catch (e) {
82
+ if ((e as {code: string}).code === "ENOENT") return null;
83
+
84
+ throw e;
85
+ }
86
+ }
@@ -0,0 +1,35 @@
1
+ import {CliCommand} from "@lodestar/utils";
2
+ import {getBeaconConfigFromArgs} from "../../config/beaconParams.js";
3
+ import {GlobalArgs} from "../../options/index.js";
4
+ import {IValidatorCliArgs} from "./options.js";
5
+ import {getSignerPubkeyHex, getSignersFromArgs} from "./signers/index.js";
6
+ import {logSigners} from "./signers/logSigners.js";
7
+
8
+ export type ReturnType = string[];
9
+
10
+ export const list: CliCommand<IValidatorCliArgs, GlobalArgs, ReturnType> = {
11
+ command: "list",
12
+
13
+ describe: "Lists the public keys of all validators",
14
+
15
+ examples: [
16
+ {
17
+ command: "validator list",
18
+ description: "List all validator public keys previously imported",
19
+ },
20
+ ],
21
+
22
+ handler: async (args) => {
23
+ const {network} = getBeaconConfigFromArgs(args);
24
+
25
+ // Ignore lockfiles to allow listing while validator client is running
26
+ args.force = true;
27
+
28
+ const signers = await getSignersFromArgs(args, network, {logger: console, signal: new AbortController().signal});
29
+
30
+ logSigners(console, signers);
31
+
32
+ // Return values for testing
33
+ return signers.map(getSignerPubkeyHex);
34
+ },
35
+ };