Skip to content

Commit

Permalink
feat(cli): export slashing protection as interchange format v5 (#5472)
Browse files Browse the repository at this point in the history
* Export slashing protection as interchange format v5

* Add flag to only export for a given subset of pubkeys

* Align voluntary-exit pubkeys description with slashing-protection export

* Log successful export as info message

* Better align import/export slashing protection logs
  • Loading branch information
nflaig authored May 8, 2023
1 parent c4a7fde commit ae3fda6
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 16 deletions.
52 changes: 42 additions & 10 deletions packages/cli/src/cmds/validator/slashingProtection/export.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import path from "node:path";
import {toHexString} from "@chainsafe/ssz";
import {InterchangeFormatVersion} from "@lodestar/validator";
import {CliCommand, writeFile600Perm} from "../../../util/index.js";
import {CliCommand, YargsError, ensure0xPrefix, isValidatePubkeyHex, writeFile600Perm} from "../../../util/index.js";
import {GlobalArgs} from "../../../options/index.js";
import {AccountValidatorArgs} from "../options.js";
import {getCliLogger, LogArgs} from "../../../util/index.js";
Expand All @@ -11,6 +12,7 @@ import {ISlashingProtectionArgs} from "./options.js";

type ExportArgs = {
file: string;
pubkeys?: string[];
};

export const exportCmd: CliCommand<ExportArgs, ISlashingProtectionArgs & AccountValidatorArgs & GlobalArgs & LogArgs> =
Expand All @@ -32,6 +34,17 @@ export const exportCmd: CliCommand<ExportArgs, ISlashingProtectionArgs & Account
demandOption: true,
type: "string",
},
pubkeys: {
description: "Export slashing protection data only for a given subset of pubkeys",
type: "array",
string: true, // Ensures the pubkey string is not automatically converted to numbers
coerce: (pubkeys: string[]): string[] =>
// Parse ["0x11,0x22"] to ["0x11", "0x22"]
pubkeys
.map((item) => item.split(","))
.flat(1)
.map(ensure0xPrefix),
},
},

handler: async (args) => {
Expand All @@ -46,9 +59,8 @@ export const exportCmd: CliCommand<ExportArgs, ISlashingProtectionArgs & Account

const {validatorsDbDir: dbPath} = getValidatorPaths(args, network);

// TODO: Allow format version and pubkeys to be customized with CLI args
const formatVersion: InterchangeFormatVersion = {version: "4", format: "complete"};
logger.info("Exporting the slashing protection logs", {...formatVersion, dbPath});
const formatVersion: InterchangeFormatVersion = {version: "5"};
logger.info("Exporting slashing protection data", {...formatVersion, dbPath});

const {slashingProtection, metadata} = getSlashingProtection(args, network, logger);

Expand All @@ -59,19 +71,39 @@ export const exportCmd: CliCommand<ExportArgs, ISlashingProtectionArgs & Account
const genesisValidatorsRoot =
(await metadata.getGenesisValidatorsRoot()) ?? (await getGenesisValidatorsRoot(args));

logger.verbose("Fetching the pubkeys from the slashingProtection db");
const pubkeys = await slashingProtection.listPubkeys();
logger.verbose("Fetching pubkeys from slashing protection db");
const allPubkeys = await slashingProtection.listPubkeys();
let pubkeysToExport = allPubkeys;

if (args.pubkeys) {
logger.verbose("Filtering by pubkeys from args", {count: args.pubkeys.length});
const filteredPubkeys = [];

for (const pubkeyHex of args.pubkeys) {
if (!isValidatePubkeyHex(pubkeyHex)) {
throw new YargsError(`Invalid pubkey ${pubkeyHex}`);
}
const existingPubkey = allPubkeys.find((pubkey) => toHexString(pubkey) === pubkeyHex);
if (!existingPubkey) {
logger.warn("Pubkey not found in slashing protection db", {pubkey: pubkeyHex});
} else {
filteredPubkeys.push(existingPubkey);
}
}

pubkeysToExport = filteredPubkeys;
}

logger.info("Starting export for pubkeys found", {pubkeys: pubkeys.length});
logger.info("Starting export for pubkeys found", {count: pubkeysToExport.length});
const interchange = await slashingProtection.exportInterchange(
genesisValidatorsRoot,
pubkeys,
pubkeysToExport,
formatVersion,
logger
);

logger.info("Writing the slashing protection logs", {file: args.file});
logger.info("Writing slashing protection data", {file: args.file});
writeFile600Perm(args.file, interchange);
logger.verbose("Export completed successfully");
logger.info("Export completed successfully");
},
};
4 changes: 2 additions & 2 deletions packages/cli/src/cmds/validator/slashingProtection/import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const importCmd: CliCommand<ImportArgs, ISlashingProtectionArgs & Account

const {validatorsDbDir: dbPath} = getValidatorPaths(args, network);

logger.info("Importing the slashing protection logs", {dbPath});
logger.info("Importing slashing protection data", {dbPath});

const {slashingProtection, metadata} = getSlashingProtection(args, network, logger);

Expand All @@ -58,7 +58,7 @@ export const importCmd: CliCommand<ImportArgs, ISlashingProtectionArgs & Account
const genesisValidatorsRoot =
(await metadata.getGenesisValidatorsRoot()) ?? (await getGenesisValidatorsRoot(args));

logger.verbose("Reading the slashing protection logs", {file: args.file});
logger.verbose("Reading slashing protection data", {file: args.file});
const interchangeStr = await fs.promises.readFile(args.file, "utf8");
const interchangeJson = JSON.parse(interchangeStr) as Interchange;

Expand Down
3 changes: 1 addition & 2 deletions packages/cli/src/cmds/validator/voluntaryExit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ If no `pubkeys` are provided, it will exit all validators that have been importe
},

pubkeys: {
description:
"Pubkeys to exit, must be available as local signers. Multiple keys have to be provided as comma-separated values.",
description: "Pubkeys to exit, must be available as local signers",
type: "array",
string: true, // Ensures the pubkey string is not automatically converted to numbers
coerce: (pubkeys: string[]): string[] =>
Expand Down
4 changes: 2 additions & 2 deletions packages/validator/src/slashingProtection/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export class SlashingProtection extends DatabaseService implements ISlashingProt
async importInterchange(interchange: Interchange, genesisValidatorsRoot: Root, logger?: Logger): Promise<void> {
const {data} = parseInterchange(interchange, genesisValidatorsRoot);
for (const validator of data) {
logger?.info(`Importing logs for Validator ${toHexString(validator.pubkey)}`);
logger?.info("Importing slashing protection", {pubkey: toHexString(validator.pubkey)});
await this.blockService.importBlocks(validator.pubkey, validator.signedBlocks);
await this.attestationService.importAttestations(validator.pubkey, validator.signedAttestations);
}
Expand All @@ -73,7 +73,7 @@ export class SlashingProtection extends DatabaseService implements ISlashingProt
): Promise<Interchange> {
const validatorData: InterchangeLodestar["data"] = [];
for (const pubkey of pubkeys) {
logger?.info(`Exporting logs for Validator ${toHexString(pubkey)}`);
logger?.info("Exporting slashing protection", {pubkey: toHexString(pubkey)});
validatorData.push({
pubkey,
signedBlocks: await this.blockService.exportBlocks(pubkey),
Expand Down

0 comments on commit ae3fda6

Please sign in to comment.