ssv-keys
Version:
Tool for splitting a validator key into a predefined threshold of shares via Shamir-Secret-Sharing (SSS), and encrypt them with a set of operator keys.
115 lines • 6.07 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.KeySharesAction = void 0;
const tslib_1 = require("tslib");
const path_1 = tslib_1.__importDefault(require("path"));
const BaseAction_1 = require("./BaseAction");
const SSVKeys_1 = require("../../lib/SSVKeys");
const KeySharesItem_1 = require("../../lib/KeyShares/KeySharesItem");
const KeyShares_1 = require("../../lib/KeyShares/KeyShares");
const base_1 = require("../../lib/exceptions/base");
const validators_1 = require("./validators");
const arguments_1 = require("./arguments");
const file_helper_1 = require("../../lib/helpers/file.helper");
const operator_1 = require("../../lib/exceptions/operator");
/**
* Command to build keyshares from user input.
*/
class KeySharesAction extends BaseAction_1.BaseAction {
static get options() {
return {
action: 'shares',
description: 'Generate shares for a list of operators from a validator keystore file',
arguments: [
arguments_1.keystoreArgument,
arguments_1.keystorePasswordArgument,
arguments_1.operatorIdsArgument,
arguments_1.operatorPublicKeysArgument,
arguments_1.outputFolderArgument,
arguments_1.ownerAddressArgument,
arguments_1.ownerNonceArgument,
],
};
}
async execute() {
this.validateKeystoreArguments(); // Validate keystore arguments
const keySharesList = await this.processKeystorePath();
const keySharesFilePath = await this.saveKeyShares(keySharesList, this.args.output_folder);
return keySharesFilePath;
}
validateKeystoreArguments() {
const hasKeystore = !!this.args.keystore;
if (!hasKeystore) {
throw new base_1.SSVKeysException('Please provide a path to the validator keystore file or to the folder containing multiple validator keystore files.');
}
}
async processKeystorePath() {
const keystorePath = (0, validators_1.sanitizePath)(String(this.args.keystore).trim());
const { files } = await (0, file_helper_1.getKeyStoreFiles)(keystorePath);
const validatedFiles = await this.validateKeystoreFiles(files);
const singleKeySharesList = await Promise.all(validatedFiles.map((file, index) => this.processFile(file, this.args.password, this.getOperators(), this.args.owner_address, this.args.owner_nonce + index)));
return singleKeySharesList;
}
async validateKeystoreFiles(files) {
const validatedFiles = [];
let failedValidation = 0;
for (const [index, file] of files.entries()) {
const isKeyStoreValid = await arguments_1.keystoreArgument.interactive.options.validate(file);
const isValidPassword = await validators_1.keystorePasswordValidator.validatePassword(this.args.password, file);
let status = '✅';
if (isKeyStoreValid === true && isValidPassword === true) {
validatedFiles.push(file);
}
else {
failedValidation++;
status = '❌';
}
const fileName = path_1.default.basename(file); // Extract the file name
process.stdout.write(`\r\n${index + 1}/${files.length} ${status} ${fileName}`);
}
process.stdout.write(`\n\n${files.length - failedValidation} of ${files.length} keystore files successfully validated. ${failedValidation} failed validation`);
process.stdout.write('\n');
return validatedFiles;
}
getOperators() {
const operatorIds = this.args.operator_ids.split(',');
const operatorKeys = this.args.operator_keys.split(',');
if (operatorIds.length !== operatorKeys.length) {
throw new operator_1.OperatorsCountsMismatchError(operatorIds, operatorKeys, 'Mismatch amount of operator ids and operator keys.');
}
if (operatorIds.includes('') || operatorKeys.includes('')) {
throw new base_1.SSVKeysException('Operator IDs or keys cannot contain empty strings.');
}
return operatorIds.map((idString, index) => {
const id = parseInt(idString, 10);
if (isNaN(id)) {
throw new base_1.SSVKeysException(`Invalid operator ID at position ${index}: ${idString}`);
}
const operatorKey = operatorKeys[index];
return { id, operatorKey };
});
}
async processFile(keystoreFilePath, password, operators, ownerAddress, ownerNonce) {
const keystoreData = await (0, file_helper_1.readFile)(keystoreFilePath);
const ssvKeys = new SSVKeys_1.SSVKeys();
const { privateKey, publicKey } = await ssvKeys.extractKeys(keystoreData, password);
const encryptedShares = await ssvKeys.buildShares(privateKey, operators);
const keySharesItem = new KeySharesItem_1.KeySharesItem();
await keySharesItem.update({ ownerAddress, ownerNonce, operators, publicKey });
await keySharesItem.buildPayload({ publicKey, operators, encryptedShares }, { ownerAddress, ownerNonce, privateKey });
return keySharesItem;
}
async saveKeyShares(keySharesItems, outputFolder) {
if (keySharesItems.length === 0) {
throw new base_1.SSVKeysException('Unable to locate valid keystore files. Please verify that the keystore files are valid and the password is correct.');
}
process.stdout.write(`\n\nGenerating Keyshares file, this might take a few minutes do not close terminal.`);
const keyShares = new KeyShares_1.KeyShares();
keySharesItems.forEach(keySharesItem => keyShares.add(keySharesItem));
const keySharesFilePath = await (0, file_helper_1.getFilePath)('keyshares', outputFolder.trim());
await (0, file_helper_1.writeFile)(keySharesFilePath, keyShares.toJson());
return keySharesFilePath;
}
}
exports.KeySharesAction = KeySharesAction;
//# sourceMappingURL=KeySharesAction.js.map