@chainsafe/lodestar 1.35.0-dev.8644a83c62 → 1.35.0-dev.894daf8b44
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/applyPreset.js +1 -1
- package/lib/applyPreset.js.map +1 -1
- 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/options.js +5 -3
- package/lib/cmds/validator/options.js.map +1 -1
- 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 +6 -0
- package/lib/options/beaconNodeOptions/api.d.ts.map +1 -0
- package/lib/options/beaconNodeOptions/api.js +14 -6
- package/lib/options/beaconNodeOptions/api.js.map +1 -1
- 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/hasher_bun.d.ts +3 -0
- package/lib/util/hasher_bun.d.ts.map +1 -0
- package/lib/util/hasher_bun.js +118 -0
- package/lib/util/hasher_bun.js.map +1 -0
- package/lib/util/hasher_nodejs.d.ts +3 -0
- package/lib/util/hasher_nodejs.d.ts.map +1 -0
- package/lib/util/hasher_nodejs.js +3 -0
- package/lib/util/hasher_nodejs.js.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/logger.js +1 -1
- package/lib/util/logger.js.map +1 -1
- 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 +33 -20
- package/src/applyPreset.ts +92 -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 +463 -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 +120 -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/hasher_bun.ts +133 -0
- package/src/util/hasher_nodejs.ts +3 -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,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
|
+
};
|