@chainsafe/lodestar 1.35.0-dev.6dd6cec93a → 1.35.0-dev.792fcea33a
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 +2 -2
- 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/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 +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/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,425 @@
|
|
|
1
|
+
import {Keystore} from "@chainsafe/bls-keystore";
|
|
2
|
+
import {SecretKey} from "@chainsafe/blst";
|
|
3
|
+
import {
|
|
4
|
+
BuilderBoostFactorData,
|
|
5
|
+
DeleteRemoteKeyStatus,
|
|
6
|
+
DeletionStatus,
|
|
7
|
+
FeeRecipientData,
|
|
8
|
+
GasLimitData,
|
|
9
|
+
GraffitiData,
|
|
10
|
+
ImportRemoteKeyStatus,
|
|
11
|
+
ImportStatus,
|
|
12
|
+
KeystoreStr,
|
|
13
|
+
ProposerConfigResponse,
|
|
14
|
+
PubkeyHex,
|
|
15
|
+
RemoteSignerDefinition,
|
|
16
|
+
ResponseStatus,
|
|
17
|
+
SignerDefinition,
|
|
18
|
+
SlashingProtectionData,
|
|
19
|
+
} from "@lodestar/api/keymanager";
|
|
20
|
+
import {KeymanagerApiMethods as Api} from "@lodestar/api/keymanager/server";
|
|
21
|
+
import {ApiError} from "@lodestar/api/server";
|
|
22
|
+
import {Epoch} from "@lodestar/types";
|
|
23
|
+
import {fromHex, isValidHttpUrl} from "@lodestar/utils";
|
|
24
|
+
import {Interchange, SignerType, Validator} from "@lodestar/validator";
|
|
25
|
+
import {getPubkeyHexFromKeystore, isValidatePubkeyHex} from "../../../util/format.js";
|
|
26
|
+
import {parseFeeRecipient} from "../../../util/index.js";
|
|
27
|
+
import {DecryptKeystoresThreadPool} from "./decryptKeystores/index.js";
|
|
28
|
+
import {IPersistedKeysBackend} from "./interface.js";
|
|
29
|
+
|
|
30
|
+
export class KeymanagerApi implements Api {
|
|
31
|
+
constructor(
|
|
32
|
+
private readonly validator: Validator,
|
|
33
|
+
private readonly persistedKeysBackend: IPersistedKeysBackend,
|
|
34
|
+
private readonly signal: AbortSignal,
|
|
35
|
+
private readonly proposerConfigWriteDisabled?: boolean
|
|
36
|
+
) {}
|
|
37
|
+
|
|
38
|
+
private checkIfProposerWriteEnabled(): void {
|
|
39
|
+
if (this.proposerConfigWriteDisabled === true) {
|
|
40
|
+
throw Error("proposerSettingsFile option activated");
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async listFeeRecipient({pubkey}: {pubkey: PubkeyHex}): ReturnType<Api["listFeeRecipient"]> {
|
|
45
|
+
this.assertValidKnownPubkey(pubkey);
|
|
46
|
+
return {data: {pubkey, ethaddress: this.validator.validatorStore.getFeeRecipient(pubkey)}};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async setFeeRecipient({pubkey, ethaddress}: FeeRecipientData): ReturnType<Api["setFeeRecipient"]> {
|
|
50
|
+
this.checkIfProposerWriteEnabled();
|
|
51
|
+
this.assertValidKnownPubkey(pubkey);
|
|
52
|
+
this.validator.validatorStore.setFeeRecipient(pubkey, parseFeeRecipient(ethaddress));
|
|
53
|
+
this.persistedKeysBackend.writeProposerConfig(pubkey, this.validator.validatorStore.getProposerConfig(pubkey));
|
|
54
|
+
return {status: 202};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async deleteFeeRecipient({pubkey}: {pubkey: PubkeyHex}): ReturnType<Api["deleteFeeRecipient"]> {
|
|
58
|
+
this.checkIfProposerWriteEnabled();
|
|
59
|
+
this.assertValidKnownPubkey(pubkey);
|
|
60
|
+
this.validator.validatorStore.deleteFeeRecipient(pubkey);
|
|
61
|
+
this.persistedKeysBackend.writeProposerConfig(pubkey, this.validator.validatorStore.getProposerConfig(pubkey));
|
|
62
|
+
return {status: 204};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async getGraffiti({pubkey}: {pubkey: PubkeyHex}): ReturnType<Api["getGraffiti"]> {
|
|
66
|
+
this.assertValidKnownPubkey(pubkey);
|
|
67
|
+
const graffiti = this.validator.validatorStore.getGraffiti(pubkey);
|
|
68
|
+
if (graffiti === undefined) {
|
|
69
|
+
throw new ApiError(404, `No graffiti for pubkey ${pubkey}`);
|
|
70
|
+
}
|
|
71
|
+
return {data: {pubkey, graffiti}};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async setGraffiti({pubkey, graffiti}: GraffitiData): ReturnType<Api["setGraffiti"]> {
|
|
75
|
+
this.checkIfProposerWriteEnabled();
|
|
76
|
+
this.assertValidKnownPubkey(pubkey);
|
|
77
|
+
this.validator.validatorStore.setGraffiti(pubkey, graffiti);
|
|
78
|
+
this.persistedKeysBackend.writeProposerConfig(pubkey, this.validator.validatorStore.getProposerConfig(pubkey));
|
|
79
|
+
return {status: 202};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async deleteGraffiti({pubkey}: {pubkey: PubkeyHex}): ReturnType<Api["deleteGraffiti"]> {
|
|
83
|
+
this.checkIfProposerWriteEnabled();
|
|
84
|
+
this.assertValidKnownPubkey(pubkey);
|
|
85
|
+
this.validator.validatorStore.deleteGraffiti(pubkey);
|
|
86
|
+
this.persistedKeysBackend.writeProposerConfig(pubkey, this.validator.validatorStore.getProposerConfig(pubkey));
|
|
87
|
+
return {status: 204};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async getGasLimit({pubkey}: {pubkey: PubkeyHex}): ReturnType<Api["getGasLimit"]> {
|
|
91
|
+
this.assertValidKnownPubkey(pubkey);
|
|
92
|
+
const gasLimit = this.validator.validatorStore.getGasLimit(pubkey);
|
|
93
|
+
return {data: {pubkey, gasLimit}};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async setGasLimit({pubkey, gasLimit}: GasLimitData): ReturnType<Api["setGasLimit"]> {
|
|
97
|
+
this.checkIfProposerWriteEnabled();
|
|
98
|
+
this.assertValidKnownPubkey(pubkey);
|
|
99
|
+
this.validator.validatorStore.setGasLimit(pubkey, gasLimit);
|
|
100
|
+
this.persistedKeysBackend.writeProposerConfig(pubkey, this.validator.validatorStore.getProposerConfig(pubkey));
|
|
101
|
+
return {status: 202};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async deleteGasLimit({pubkey}: {pubkey: PubkeyHex}): ReturnType<Api["deleteGasLimit"]> {
|
|
105
|
+
this.checkIfProposerWriteEnabled();
|
|
106
|
+
this.assertValidKnownPubkey(pubkey);
|
|
107
|
+
this.validator.validatorStore.deleteGasLimit(pubkey);
|
|
108
|
+
this.persistedKeysBackend.writeProposerConfig(pubkey, this.validator.validatorStore.getProposerConfig(pubkey));
|
|
109
|
+
return {status: 204};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async listKeys(): ReturnType<Api["listKeys"]> {
|
|
113
|
+
const localKeys = this.validator.validatorStore
|
|
114
|
+
.votingPubkeys()
|
|
115
|
+
.filter((pubkey) => this.validator.validatorStore.getSigner(pubkey)?.type === SignerType.Local);
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
data: localKeys.map((pubkey) => ({
|
|
119
|
+
validatingPubkey: pubkey,
|
|
120
|
+
derivationPath: "",
|
|
121
|
+
readonly: false,
|
|
122
|
+
})),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async importKeystores({
|
|
127
|
+
keystores,
|
|
128
|
+
passwords,
|
|
129
|
+
slashingProtection,
|
|
130
|
+
}: {
|
|
131
|
+
keystores: KeystoreStr[];
|
|
132
|
+
passwords: string[];
|
|
133
|
+
slashingProtection?: SlashingProtectionData;
|
|
134
|
+
}): ReturnType<Api["importKeystores"]> {
|
|
135
|
+
if (slashingProtection) {
|
|
136
|
+
// The arguments to this function is passed in within the body of an HTTP request
|
|
137
|
+
// hence fastify will parse it into an object before this function is called.
|
|
138
|
+
// Even though the slashingProtection is typed as SlashingProtectionData,
|
|
139
|
+
// at runtime, when the handler for the request is selected, it would see slashingProtection
|
|
140
|
+
// as an object, hence trying to parse it using JSON.parse won't work. Instead, we cast straight to Interchange
|
|
141
|
+
const interchange = ensureJSON<Interchange>(slashingProtection);
|
|
142
|
+
await this.validator.importInterchange(interchange);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const statuses: {status: ImportStatus; message?: string}[] = [];
|
|
146
|
+
const decryptKeystores = new DecryptKeystoresThreadPool(keystores.length, this.signal);
|
|
147
|
+
|
|
148
|
+
for (let i = 0; i < keystores.length; i++) {
|
|
149
|
+
try {
|
|
150
|
+
const keystoreStr = keystores[i];
|
|
151
|
+
const password = passwords[i];
|
|
152
|
+
if (password === undefined) {
|
|
153
|
+
throw new ApiError(400, `No password for keystores[${i}]`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const keystore = Keystore.parse(keystoreStr);
|
|
157
|
+
const pubkeyHex = getPubkeyHexFromKeystore(keystore);
|
|
158
|
+
|
|
159
|
+
// Check for duplicates and skip keystore before decrypting
|
|
160
|
+
if (this.validator.validatorStore.hasVotingPubkey(pubkeyHex)) {
|
|
161
|
+
statuses[i] = {status: ImportStatus.duplicate};
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
decryptKeystores.queue(
|
|
166
|
+
{keystoreStr, password},
|
|
167
|
+
async (secretKeyBytes: Uint8Array) => {
|
|
168
|
+
const secretKey = SecretKey.fromBytes(secretKeyBytes);
|
|
169
|
+
|
|
170
|
+
// Persist the key to disk for restarts, before adding to in-memory store
|
|
171
|
+
// If the keystore exist and has a lock it will throw
|
|
172
|
+
this.persistedKeysBackend.writeKeystore({
|
|
173
|
+
keystoreStr,
|
|
174
|
+
password,
|
|
175
|
+
// Lock immediately since it's gonna be used
|
|
176
|
+
lockBeforeWrite: true,
|
|
177
|
+
// Always write, even if it's already persisted for consistency.
|
|
178
|
+
// The in-memory validatorStore is the ground truth to decide duplicates
|
|
179
|
+
persistIfDuplicate: true,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Add to in-memory store to start validating immediately
|
|
183
|
+
await this.validator.validatorStore.addSigner({type: SignerType.Local, secretKey});
|
|
184
|
+
|
|
185
|
+
statuses[i] = {status: ImportStatus.imported};
|
|
186
|
+
},
|
|
187
|
+
(e: Error) => {
|
|
188
|
+
statuses[i] = {status: ImportStatus.error, message: e.message};
|
|
189
|
+
}
|
|
190
|
+
);
|
|
191
|
+
} catch (e) {
|
|
192
|
+
statuses[i] = {status: ImportStatus.error, message: (e as Error).message};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
await decryptKeystores.completed();
|
|
197
|
+
|
|
198
|
+
return {data: statuses};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async deleteKeys({pubkeys}: {pubkeys: PubkeyHex[]}): ReturnType<Api["deleteKeys"]> {
|
|
202
|
+
const deletedKey: boolean[] = [];
|
|
203
|
+
const statuses = new Array<{status: DeletionStatus; message?: string}>(pubkeys.length);
|
|
204
|
+
|
|
205
|
+
for (let i = 0; i < pubkeys.length; i++) {
|
|
206
|
+
try {
|
|
207
|
+
const pubkeyHex = pubkeys[i];
|
|
208
|
+
|
|
209
|
+
if (!isValidatePubkeyHex(pubkeyHex)) {
|
|
210
|
+
throw new ApiError(400, `Invalid pubkey ${pubkeyHex}`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Skip unknown keys or remote signers
|
|
214
|
+
const signer = this.validator.validatorStore.getSigner(pubkeyHex);
|
|
215
|
+
if (signer && signer.type === SignerType.Local) {
|
|
216
|
+
// Remove key from live local signer
|
|
217
|
+
deletedKey[i] = this.validator.validatorStore.removeSigner(pubkeyHex);
|
|
218
|
+
|
|
219
|
+
// Remove key from block duties
|
|
220
|
+
// Remove from attestation duties
|
|
221
|
+
// Remove from Sync committee duties
|
|
222
|
+
// Remove from indices
|
|
223
|
+
this.validator.removeDutiesForKey(pubkeyHex);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Attempts to delete everything first, and returns status.
|
|
227
|
+
// This unlocks the keystore, so perform after deleting from in-memory store
|
|
228
|
+
const diskDeleteStatus = this.persistedKeysBackend.deleteKeystore(pubkeyHex);
|
|
229
|
+
|
|
230
|
+
if (diskDeleteStatus) {
|
|
231
|
+
// TODO: What if the diskDeleteStatus status is inconsistent?
|
|
232
|
+
deletedKey[i] = true;
|
|
233
|
+
}
|
|
234
|
+
} catch (e) {
|
|
235
|
+
statuses[i] = {status: DeletionStatus.error, message: (e as Error).message};
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const pubkeysBytes = pubkeys.map((pubkeyHex) => fromHex(pubkeyHex));
|
|
240
|
+
|
|
241
|
+
const interchangeV5 = await this.validator.exportInterchange(pubkeysBytes, {
|
|
242
|
+
version: "5",
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// After exporting slashing protection data in bulk, render the status
|
|
246
|
+
const pubkeysWithSlashingProtectionData = new Set(interchangeV5.data.map((data) => data.pubkey));
|
|
247
|
+
for (let i = 0; i < pubkeys.length; i++) {
|
|
248
|
+
if (statuses[i]?.status === DeletionStatus.error) {
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
const status = deletedKey[i]
|
|
252
|
+
? DeletionStatus.deleted
|
|
253
|
+
: pubkeysWithSlashingProtectionData.has(pubkeys[i])
|
|
254
|
+
? DeletionStatus.not_active
|
|
255
|
+
: DeletionStatus.not_found;
|
|
256
|
+
statuses[i] = {status};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
data: {
|
|
261
|
+
statuses,
|
|
262
|
+
slashingProtection: JSON.stringify(interchangeV5),
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async listRemoteKeys(): ReturnType<Api["listRemoteKeys"]> {
|
|
268
|
+
const remoteKeys: SignerDefinition[] = [];
|
|
269
|
+
|
|
270
|
+
for (const pubkeyHex of this.validator.validatorStore.votingPubkeys()) {
|
|
271
|
+
const signer = this.validator.validatorStore.getSigner(pubkeyHex);
|
|
272
|
+
if (signer && signer.type === SignerType.Remote) {
|
|
273
|
+
remoteKeys.push({pubkey: signer.pubkey, url: signer.url, readonly: false});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
data: remoteKeys,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async importRemoteKeys({
|
|
283
|
+
remoteSigners,
|
|
284
|
+
}: {
|
|
285
|
+
remoteSigners: RemoteSignerDefinition[];
|
|
286
|
+
}): ReturnType<Api["importRemoteKeys"]> {
|
|
287
|
+
const importPromises = remoteSigners.map(async ({pubkey, url}): Promise<ResponseStatus<ImportRemoteKeyStatus>> => {
|
|
288
|
+
try {
|
|
289
|
+
if (!isValidatePubkeyHex(pubkey)) {
|
|
290
|
+
throw new ApiError(400, `Invalid pubkey ${pubkey}`);
|
|
291
|
+
}
|
|
292
|
+
if (!isValidHttpUrl(url)) {
|
|
293
|
+
throw new ApiError(400, `Invalid URL ${url}`);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Check if key exists
|
|
297
|
+
if (this.validator.validatorStore.hasVotingPubkey(pubkey)) {
|
|
298
|
+
return {status: ImportRemoteKeyStatus.duplicate};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Else try to add it
|
|
302
|
+
|
|
303
|
+
await this.validator.validatorStore.addSigner({type: SignerType.Remote, pubkey, url});
|
|
304
|
+
|
|
305
|
+
this.persistedKeysBackend.writeRemoteKey({
|
|
306
|
+
pubkey,
|
|
307
|
+
url,
|
|
308
|
+
// Always write, even if it's already persisted for consistency.
|
|
309
|
+
// The in-memory validatorStore is the ground truth to decide duplicates
|
|
310
|
+
persistIfDuplicate: true,
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
return {status: ImportRemoteKeyStatus.imported};
|
|
314
|
+
} catch (e) {
|
|
315
|
+
return {status: ImportRemoteKeyStatus.error, message: (e as Error).message};
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
return {
|
|
320
|
+
data: await Promise.all(importPromises),
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
async deleteRemoteKeys({pubkeys}: {pubkeys: PubkeyHex[]}): ReturnType<Api["deleteRemoteKeys"]> {
|
|
325
|
+
const results = pubkeys.map((pubkeyHex): ResponseStatus<DeleteRemoteKeyStatus> => {
|
|
326
|
+
try {
|
|
327
|
+
if (!isValidatePubkeyHex(pubkeyHex)) {
|
|
328
|
+
throw new ApiError(400, `Invalid pubkey ${pubkeyHex}`);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const signer = this.validator.validatorStore.getSigner(pubkeyHex);
|
|
332
|
+
|
|
333
|
+
// Remove key from live local signer
|
|
334
|
+
const deletedFromMemory =
|
|
335
|
+
signer && signer.type === SignerType.Remote ? this.validator.validatorStore.removeSigner(pubkeyHex) : false;
|
|
336
|
+
|
|
337
|
+
if (deletedFromMemory) {
|
|
338
|
+
// Remove duties if key was deleted from in-memory store
|
|
339
|
+
this.validator.removeDutiesForKey(pubkeyHex);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const deletedFromDisk = this.persistedKeysBackend.deleteRemoteKey(pubkeyHex);
|
|
343
|
+
|
|
344
|
+
return {
|
|
345
|
+
status:
|
|
346
|
+
deletedFromMemory || deletedFromDisk ? DeleteRemoteKeyStatus.deleted : DeleteRemoteKeyStatus.not_found,
|
|
347
|
+
};
|
|
348
|
+
} catch (e) {
|
|
349
|
+
return {status: DeleteRemoteKeyStatus.error, message: (e as Error).message};
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
return {
|
|
354
|
+
data: results,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
async getBuilderBoostFactor({pubkey}: {pubkey: PubkeyHex}): ReturnType<Api["getBuilderBoostFactor"]> {
|
|
359
|
+
this.assertValidKnownPubkey(pubkey);
|
|
360
|
+
const builderBoostFactor = this.validator.validatorStore.getBuilderBoostFactor(pubkey);
|
|
361
|
+
return {data: {pubkey, builderBoostFactor}};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
async setBuilderBoostFactor({
|
|
365
|
+
pubkey,
|
|
366
|
+
builderBoostFactor,
|
|
367
|
+
}: BuilderBoostFactorData): ReturnType<Api["setBuilderBoostFactor"]> {
|
|
368
|
+
this.checkIfProposerWriteEnabled();
|
|
369
|
+
this.assertValidKnownPubkey(pubkey);
|
|
370
|
+
this.validator.validatorStore.setBuilderBoostFactor(pubkey, builderBoostFactor);
|
|
371
|
+
this.persistedKeysBackend.writeProposerConfig(pubkey, this.validator.validatorStore.getProposerConfig(pubkey));
|
|
372
|
+
return {status: 202};
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
async deleteBuilderBoostFactor({pubkey}: {pubkey: PubkeyHex}): ReturnType<Api["deleteBuilderBoostFactor"]> {
|
|
376
|
+
this.checkIfProposerWriteEnabled();
|
|
377
|
+
this.validator.validatorStore.deleteBuilderBoostFactor(pubkey);
|
|
378
|
+
this.persistedKeysBackend.writeProposerConfig(pubkey, this.validator.validatorStore.getProposerConfig(pubkey));
|
|
379
|
+
return {status: 204};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
async getProposerConfig({pubkey}: {pubkey: PubkeyHex}): ReturnType<Api["getProposerConfig"]> {
|
|
383
|
+
this.assertValidKnownPubkey(pubkey);
|
|
384
|
+
|
|
385
|
+
const config = this.validator.validatorStore.getProposerConfig(pubkey);
|
|
386
|
+
|
|
387
|
+
const data: ProposerConfigResponse = {
|
|
388
|
+
...config,
|
|
389
|
+
builder: config?.builder
|
|
390
|
+
? {
|
|
391
|
+
...config.builder,
|
|
392
|
+
// Default JSON serialization can't handle BigInt
|
|
393
|
+
boostFactor: config.builder.boostFactor ? config.builder.boostFactor.toString() : undefined,
|
|
394
|
+
}
|
|
395
|
+
: undefined,
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
return {data};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
async signVoluntaryExit({pubkey, epoch}: {pubkey: PubkeyHex; epoch?: Epoch}): ReturnType<Api["signVoluntaryExit"]> {
|
|
402
|
+
this.assertValidKnownPubkey(pubkey);
|
|
403
|
+
return {data: await this.validator.signVoluntaryExit(pubkey, epoch)};
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
private assertValidKnownPubkey(pubkey: PubkeyHex): void {
|
|
407
|
+
if (!isValidatePubkeyHex(pubkey)) {
|
|
408
|
+
throw new ApiError(400, `Invalid pubkey ${pubkey}`);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (!this.validator.validatorStore.hasVotingPubkey(pubkey)) {
|
|
412
|
+
throw new ApiError(404, `Validator pubkey ${pubkey} not known`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Given a variable with JSON that maybe stringified or not, return parsed JSON
|
|
419
|
+
*/
|
|
420
|
+
function ensureJSON<T>(strOrJson: string | T): T {
|
|
421
|
+
if (typeof strOrJson === "string") {
|
|
422
|
+
return JSON.parse(strOrJson) as T;
|
|
423
|
+
}
|
|
424
|
+
return strOrJson;
|
|
425
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {PubkeyHex, SignerDefinition} from "@lodestar/api/keymanager";
|
|
2
|
+
import {ProposerConfig} from "@lodestar/validator";
|
|
3
|
+
|
|
4
|
+
export type LocalKeystoreDefinition = {
|
|
5
|
+
keystorePath: string;
|
|
6
|
+
password: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export interface IPersistedKeysBackend {
|
|
10
|
+
readAllKeystores(): LocalKeystoreDefinition[];
|
|
11
|
+
|
|
12
|
+
/** Returns true some item is written to disk */
|
|
13
|
+
writeKeystore(args: {
|
|
14
|
+
keystoreStr: string;
|
|
15
|
+
password: string;
|
|
16
|
+
lockBeforeWrite: boolean;
|
|
17
|
+
persistIfDuplicate: boolean;
|
|
18
|
+
}): boolean;
|
|
19
|
+
|
|
20
|
+
/** Returns true some item is deleted from disk */
|
|
21
|
+
deleteKeystore(pubkey: PubkeyHex): boolean;
|
|
22
|
+
|
|
23
|
+
readAllRemoteKeys(): SignerDefinition[];
|
|
24
|
+
|
|
25
|
+
/** Returns true some item is written to disk */
|
|
26
|
+
writeRemoteKey(args: {pubkey: PubkeyHex; url: string; persistIfDuplicate: boolean}): boolean;
|
|
27
|
+
|
|
28
|
+
/** Returns true some item is deleted from disk */
|
|
29
|
+
deleteRemoteKey(pubkey: PubkeyHex): boolean;
|
|
30
|
+
|
|
31
|
+
writeProposerConfig(pubkey: PubkeyHex, proposerConfig: ProposerConfig | null): void;
|
|
32
|
+
deleteProposerConfig(pubkeyHex: PubkeyHex): void;
|
|
33
|
+
readProposerConfigs(): {[index: string]: ProposerConfig};
|
|
34
|
+
deleteProposerConfigs(): void;
|
|
35
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {Keystore} from "@chainsafe/bls-keystore";
|
|
4
|
+
import {SecretKey} from "@chainsafe/blst";
|
|
5
|
+
import {fromHex, toHex, toPubkeyHex} from "@lodestar/utils";
|
|
6
|
+
import {SignerLocal, SignerType} from "@lodestar/validator";
|
|
7
|
+
import {writeFile600Perm} from "../../../util/file.js";
|
|
8
|
+
import {lockFilepath, unlockFilepath} from "../../../util/lockfile.js";
|
|
9
|
+
import {LocalKeystoreDefinition} from "./interface.js";
|
|
10
|
+
|
|
11
|
+
export async function loadKeystoreCache(
|
|
12
|
+
cacheFilepath: string,
|
|
13
|
+
keystoreDefinitions: LocalKeystoreDefinition[]
|
|
14
|
+
): Promise<SignerLocal[]> {
|
|
15
|
+
const keystores: Keystore[] = [];
|
|
16
|
+
const passwords: string[] = [];
|
|
17
|
+
for (const {keystorePath, password} of keystoreDefinitions) {
|
|
18
|
+
keystores.push(Keystore.parse(fs.readFileSync(keystorePath, "utf8")));
|
|
19
|
+
passwords.push(password);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (keystores.length !== passwords.length) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`Number of keystores and passwords must be equal. keystores=${keystores.length}, passwords=${passwords.length}`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!fs.existsSync(cacheFilepath)) {
|
|
29
|
+
throw new Error(`Cache file ${cacheFilepath} does not exists.`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
lockFilepath(cacheFilepath);
|
|
33
|
+
|
|
34
|
+
const password = passwords.join("");
|
|
35
|
+
// We can't use Keystore.parse as it validates the `encrypted message` to be only 32 bytes.
|
|
36
|
+
const keystore = new Keystore(JSON.parse(fs.readFileSync(cacheFilepath, "utf8")));
|
|
37
|
+
const secretKeyConcatenatedBytes = await keystore.decrypt(password);
|
|
38
|
+
|
|
39
|
+
const result: SignerLocal[] = [];
|
|
40
|
+
for (const [index, k] of keystores.entries()) {
|
|
41
|
+
const secretKeyBytes = Uint8Array.prototype.slice.call(secretKeyConcatenatedBytes, index * 32, (index + 1) * 32);
|
|
42
|
+
const secretKey = SecretKey.fromBytes(secretKeyBytes);
|
|
43
|
+
const publicKey = secretKey.toPublicKey().toBytes();
|
|
44
|
+
|
|
45
|
+
if (toPubkeyHex(publicKey) !== toPubkeyHex(fromHex(k.pubkey))) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`Keystore ${k.uuid} does not match the expected pubkey. expected=${toPubkeyHex(fromHex(k.pubkey))}, found=${toHex(
|
|
48
|
+
publicKey
|
|
49
|
+
)}`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
result.push({
|
|
54
|
+
type: SignerType.Local,
|
|
55
|
+
secretKey,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
unlockFilepath(cacheFilepath);
|
|
60
|
+
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function writeKeystoreCache(
|
|
65
|
+
cacheFilepath: string,
|
|
66
|
+
signers: SignerLocal[],
|
|
67
|
+
passwords: string[]
|
|
68
|
+
): Promise<void> {
|
|
69
|
+
if (signers.length !== passwords.length) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
`Number of signers and passwords must be equal. signers=${signers.length}, passwords=${passwords.length}`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
const secretKeys = signers.map((s) => s.secretKey.toBytes());
|
|
75
|
+
const publicKeys = signers.map((s) => s.secretKey.toPublicKey().toBytes());
|
|
76
|
+
const password = passwords.join("");
|
|
77
|
+
const secretKeyConcatenatedBytes = Buffer.concat(secretKeys);
|
|
78
|
+
const publicConcatenatedBytes = Buffer.concat(publicKeys);
|
|
79
|
+
const keystore = await Keystore.create(password, secretKeyConcatenatedBytes, publicConcatenatedBytes, cacheFilepath);
|
|
80
|
+
if (!fs.existsSync(path.dirname(cacheFilepath))) fs.mkdirSync(path.dirname(cacheFilepath), {recursive: true});
|
|
81
|
+
lockFilepath(cacheFilepath);
|
|
82
|
+
writeFile600Perm(cacheFilepath, keystore.stringify());
|
|
83
|
+
unlockFilepath(cacheFilepath);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function clearKeystoreCache(cacheFilepath: string): Promise<void> {
|
|
87
|
+
if (fs.existsSync(cacheFilepath)) {
|
|
88
|
+
unlockFilepath(cacheFilepath);
|
|
89
|
+
fs.unlinkSync(cacheFilepath);
|
|
90
|
+
}
|
|
91
|
+
}
|