diff --git a/packages/api/src/builder/routes.ts b/packages/api/src/builder/routes.ts index 297c4fc5e9a9..3d74101bb046 100644 --- a/packages/api/src/builder/routes.ts +++ b/packages/api/src/builder/routes.ts @@ -13,6 +13,7 @@ import { } from "@lodestar/types"; import {ForkName, isForkBlobs} from "@lodestar/params"; import {ChainForkConfig} from "@lodestar/config"; +import {toPubkeyHex} from "@lodestar/utils"; import {Endpoint, RouteDefinitions, Schema} from "../utils/index.js"; import {MetaHeader, VersionCodec, VersionMeta} from "../utils/metadata.js"; @@ -105,7 +106,7 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions ({ - params: {slot, parent_hash: toHexString(parentHash), pubkey: toHexString(proposerPubKey)}, + params: {slot, parent_hash: toHexString(parentHash), pubkey: toPubkeyHex(proposerPubKey)}, }), parseReq: ({params}) => ({ slot: params.slot, diff --git a/packages/cli/src/cmds/validator/keymanager/keystoreCache.ts b/packages/cli/src/cmds/validator/keymanager/keystoreCache.ts index 85b1702892ee..2997a6b6b113 100644 --- a/packages/cli/src/cmds/validator/keymanager/keystoreCache.ts +++ b/packages/cli/src/cmds/validator/keymanager/keystoreCache.ts @@ -3,7 +3,7 @@ import path from "node:path"; import {Keystore} from "@chainsafe/bls-keystore"; import {SecretKey} from "@chainsafe/blst"; import {SignerLocal, SignerType} from "@lodestar/validator"; -import {fromHex, toHex} from "@lodestar/utils"; +import {fromHex, toHex, toPubkeyHex} from "@lodestar/utils"; import {writeFile600Perm} from "../../../util/file.js"; import {lockFilepath, unlockFilepath} from "../../../util/lockfile.js"; import {LocalKeystoreDefinition} from "./interface.js"; @@ -42,9 +42,9 @@ export async function loadKeystoreCache( const secretKey = SecretKey.fromBytes(secretKeyBytes); const publicKey = secretKey.toPublicKey().toBytes(); - if (toHex(publicKey) !== toHex(fromHex(k.pubkey))) { + if (toPubkeyHex(publicKey) !== toPubkeyHex(fromHex(k.pubkey))) { throw new Error( - `Keystore ${k.uuid} does not match the expected pubkey. expected=${toHex(fromHex(k.pubkey))}, found=${toHex( + `Keystore ${k.uuid} does not match the expected pubkey. expected=${toPubkeyHex(fromHex(k.pubkey))}, found=${toHex( publicKey )}` ); diff --git a/packages/cli/src/cmds/validator/slashingProtection/export.ts b/packages/cli/src/cmds/validator/slashingProtection/export.ts index 7d1a4f8e6e2f..c18b020f5782 100644 --- a/packages/cli/src/cmds/validator/slashingProtection/export.ts +++ b/packages/cli/src/cmds/validator/slashingProtection/export.ts @@ -1,8 +1,7 @@ import path from "node:path"; -import {toHexString} from "@chainsafe/ssz"; import {InterchangeFormatVersion} from "@lodestar/validator"; import {getNodeLogger} from "@lodestar/logger/node"; -import {CliCommand} from "@lodestar/utils"; +import {CliCommand, toPubkeyHex} from "@lodestar/utils"; import {YargsError, ensure0xPrefix, isValidatePubkeyHex, writeFile600Perm} from "../../../util/index.js"; import {parseLoggerArgs} from "../../../util/logger.js"; import {GlobalArgs} from "../../../options/index.js"; @@ -86,7 +85,7 @@ export const exportCmd: CliCommand toHexString(pubkey) === pubkeyHex); + const existingPubkey = allPubkeys.find((pubkey) => toPubkeyHex(pubkey) === pubkeyHex); if (!existingPubkey) { logger.warn("Pubkey not found in slashing protection db", {pubkey: pubkeyHex}); } else { diff --git a/packages/cli/src/cmds/validator/voluntaryExit.ts b/packages/cli/src/cmds/validator/voluntaryExit.ts index 279076b619f2..a399a763662d 100644 --- a/packages/cli/src/cmds/validator/voluntaryExit.ts +++ b/packages/cli/src/cmds/validator/voluntaryExit.ts @@ -8,7 +8,7 @@ import { } from "@lodestar/state-transition"; import {createBeaconConfig, BeaconConfig} from "@lodestar/config"; import {phase0, ssz, ValidatorIndex, Epoch} from "@lodestar/types"; -import {CliCommand, fromHex, toHex} from "@lodestar/utils"; +import {CliCommand, fromHex, toPubkeyHex} from "@lodestar/utils"; import {externalSignerPostSignature, SignableMessageType, Signer, SignerType} from "@lodestar/validator"; import {ApiClient, getClient} from "@lodestar/api"; import {ensure0xPrefix, YargsError, wrapError} from "../../util/index.js"; @@ -209,7 +209,7 @@ async function resolveValidatorIndexes(client: ApiClient, signersToExit: SignerP const validators = (await client.beacon.getStateValidators({stateId: "head", validatorIds: pubkeys})).value(); - const dataByPubkey = new Map(validators.map((item) => [toHex(item.validator.pubkey), item])); + const dataByPubkey = new Map(validators.map((item) => [toPubkeyHex(item.validator.pubkey), item])); return signersToExit.map(({signer, pubkey}) => { const item = dataByPubkey.get(pubkey); diff --git a/packages/flare/src/cmds/selfSlashAttester.ts b/packages/flare/src/cmds/selfSlashAttester.ts index e29a956a9306..a37c6c765bd3 100644 --- a/packages/flare/src/cmds/selfSlashAttester.ts +++ b/packages/flare/src/cmds/selfSlashAttester.ts @@ -4,7 +4,7 @@ import {AttesterSlashing, phase0, ssz} from "@lodestar/types"; import {config as chainConfig} from "@lodestar/config/default"; import {createBeaconConfig, BeaconConfig} from "@lodestar/config"; import {DOMAIN_BEACON_ATTESTER, MAX_VALIDATORS_PER_COMMITTEE} from "@lodestar/params"; -import {CliCommand, toHexString} from "@lodestar/utils"; +import {CliCommand, toPubkeyHex} from "@lodestar/utils"; import {computeSigningRoot} from "@lodestar/state-transition"; import {deriveSecretKeys, SecretKeysArgs, secretKeysOptions} from "../util/deriveSecretKeys.js"; @@ -90,7 +90,7 @@ export async function selfSlashAttesterHandler(args: SelfSlashArgs): Promise; @@ -82,7 +83,7 @@ function computeSyncCommitteeIndices( for (const pubkey of pubkeys) { const validatorIndex = pubkey2index.get(pubkey); if (validatorIndex === undefined) { - throw Error(`SyncCommittee pubkey is unknown ${toHexString(pubkey)}`); + throw Error(`SyncCommittee pubkey is unknown ${toPubkeyHex(pubkey)}`); } validatorIndices.push(validatorIndex); diff --git a/packages/utils/src/bytes/browser.ts b/packages/utils/src/bytes/browser.ts index e6c04f79835f..f610e2912c04 100644 --- a/packages/utils/src/bytes/browser.ts +++ b/packages/utils/src/bytes/browser.ts @@ -1,26 +1,20 @@ +// "0".charCodeAt(0) = 48 +const CHAR_CODE_0 = 48; +// "x".charCodeAt(0) = 120 +const CHAR_CODE_X = 120; + export function toHex(bytes: Uint8Array): string { const charCodes = new Array(bytes.length * 2 + 2); - charCodes[0] = 48; - charCodes[1] = 120; - - for (let i = 0; i < bytes.length; i++) { - const byte = bytes[i]; - const first = (byte & 0xf0) >> 4; - const second = byte & 0x0f; + charCodes[0] = CHAR_CODE_0; + charCodes[1] = CHAR_CODE_X; - // "0".charCodeAt(0) = 48 - // "a".charCodeAt(0) = 97 => delta = 87 - charCodes[2 + 2 * i] = first < 10 ? first + 48 : first + 87; - charCodes[2 + 2 * i + 1] = second < 10 ? second + 48 : second + 87; - } + bytesIntoCharCodes(bytes, charCodes); return String.fromCharCode(...charCodes); } const rootCharCodes = new Array(32 * 2 + 2); -// "0".charCodeAt(0) -rootCharCodes[0] = 48; -// "x".charCodeAt(0) -rootCharCodes[1] = 120; +rootCharCodes[0] = CHAR_CODE_0; +rootCharCodes[1] = CHAR_CODE_X; /** * Convert a Uint8Array, length 32, to 0x-prefixed hex string @@ -30,17 +24,24 @@ export function toRootHex(root: Uint8Array): string { throw Error(`Expect root to be 32 bytes, got ${root.length}`); } - for (let i = 0; i < root.length; i++) { - const byte = root[i]; - const first = (byte & 0xf0) >> 4; - const second = byte & 0x0f; + bytesIntoCharCodes(root, rootCharCodes); + return String.fromCharCode(...rootCharCodes); +} - // "0".charCodeAt(0) = 48 - // "a".charCodeAt(0) = 97 => delta = 87 - rootCharCodes[2 + 2 * i] = first < 10 ? first + 48 : first + 87; - rootCharCodes[2 + 2 * i + 1] = second < 10 ? second + 48 : second + 87; +const pubkeyCharCodes = new Array(48 * 2 + 2); +pubkeyCharCodes[0] = CHAR_CODE_0; +pubkeyCharCodes[1] = CHAR_CODE_X; + +/** + * Convert a Uint8Array, length 48, to 0x-prefixed hex string + */ +export function toPubkeyHex(pubkey: Uint8Array): string { + if (pubkey.length !== CHAR_CODE_0) { + throw Error(`Expect pubkey to be 48 bytes, got ${pubkey.length}`); } - return String.fromCharCode(...rootCharCodes); + + bytesIntoCharCodes(pubkey, pubkeyCharCodes); + return String.fromCharCode(...pubkeyCharCodes); } export function fromHex(hex: string): Uint8Array { @@ -64,3 +65,23 @@ export function fromHex(hex: string): Uint8Array { } return bytes; } + +/** + * Populate charCodes from bytes. Note that charCodes index 0 and 1 ("0x") are not populated. + */ +function bytesIntoCharCodes(bytes: Uint8Array, charCodes: number[]): void { + if (bytes.length * 2 + 2 !== charCodes.length) { + throw Error(`Expect charCodes to be of length ${bytes.length * 2 + 2}, got ${charCodes.length}`); + } + + for (let i = 0; i < bytes.length; i++) { + const byte = bytes[i]; + const first = (byte & 0xf0) >> 4; + const second = byte & 0x0f; + + // "0".charCodeAt(0) = 48 + // "a".charCodeAt(0) = 97 => delta = 87 + charCodes[2 + 2 * i] = first < 10 ? first + 48 : first + 87; + charCodes[2 + 2 * i + 1] = second < 10 ? second + 48 : second + 87; + } +} diff --git a/packages/utils/src/bytes/index.ts b/packages/utils/src/bytes/index.ts index fe6a9fc40e54..a079764738b7 100644 --- a/packages/utils/src/bytes/index.ts +++ b/packages/utils/src/bytes/index.ts @@ -1,14 +1,26 @@ -import {toHex as browserToHex, toRootHex as browserToRootHex, fromHex as browserFromHex} from "./browser.js"; -import {toHex as nodeToHex, toRootHex as nodeToRootHex, fromHex as nodeFromHex} from "./nodejs.js"; +import { + toHex as browserToHex, + toRootHex as browserToRootHex, + fromHex as browserFromHex, + toPubkeyHex as browserToPubkeyHex, +} from "./browser.js"; +import { + toHex as nodeToHex, + toRootHex as nodeToRootHex, + fromHex as nodeFromHex, + toPubkeyHex as nodeToPubkeyHex, +} from "./nodejs.js"; let toHex = browserToHex; let toRootHex = browserToRootHex; +let toPubkeyHex = browserToPubkeyHex; let fromHex = browserFromHex; if (typeof Buffer !== "undefined") { toHex = nodeToHex; toRootHex = nodeToRootHex; + toPubkeyHex = nodeToPubkeyHex; fromHex = nodeFromHex; } -export {toHex, toRootHex, fromHex}; +export {toHex, toRootHex, toPubkeyHex, fromHex}; diff --git a/packages/utils/src/bytes/nodejs.ts b/packages/utils/src/bytes/nodejs.ts index 7f0fe50d2a4a..636f49bd8e76 100644 --- a/packages/utils/src/bytes/nodejs.ts +++ b/packages/utils/src/bytes/nodejs.ts @@ -27,6 +27,22 @@ export function toRootHex(root: Uint8Array): string { return `0x${rootBuf.toString("hex")}`; } +// Shared buffer to convert pubkey to hex +let pubkeyBuf: Buffer | undefined; + +export function toPubkeyHex(pubkey: Uint8Array): string { + if (pubkey.length !== 48) { + throw Error(`Expect pubkey to be 48 bytes, got ${pubkey.length}`); + } + + if (pubkeyBuf === undefined) { + pubkeyBuf = Buffer.alloc(48); + } + + pubkeyBuf.set(pubkey); + return `0x${pubkeyBuf.toString("hex")}`; +} + export function fromHex(hex: string): Uint8Array { const b = Buffer.from(hex.replace("0x", ""), "hex"); return new Uint8Array(b.buffer, b.byteOffset, b.length); diff --git a/packages/utils/test/unit/bytes.test.ts b/packages/utils/test/unit/bytes.test.ts index aefa3e240954..05789b839cfd 100644 --- a/packages/utils/test/unit/bytes.test.ts +++ b/packages/utils/test/unit/bytes.test.ts @@ -1,5 +1,5 @@ import {describe, it, expect} from "vitest"; -import {intToBytes, bytesToInt, toHex, fromHex, toHexString, toRootHex} from "../../src/index.js"; +import {intToBytes, bytesToInt, toHex, fromHex, toHexString, toRootHex, toPubkeyHex} from "../../src/index.js"; describe("intToBytes", () => { const zeroedArray = (length: number): number[] => Array.from({length}, () => 0); @@ -80,6 +80,25 @@ describe("toRootHex", () => { } }); +describe("toPubkeyHex", () => { + const testCases: {input: Uint8Array; output: string}[] = [ + { + input: new Uint8Array(Array.from({length: 48}, (_, i) => i)), + output: "0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f", + }, + { + input: new Uint8Array(Array.from({length: 48}, () => 0)), + output: "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + }, + ]; + + for (const {input, output} of testCases) { + it(`should convert root to hex string ${output}`, () => { + expect(toPubkeyHex(input)).toBe(output); + }); + } +}); + describe("fromHex", () => { const testCases: {input: string; output: Buffer | Uint8Array}[] = [ { diff --git a/packages/validator/src/services/attestationDuties.ts b/packages/validator/src/services/attestationDuties.ts index ea82bf0a4c72..83838afe1492 100644 --- a/packages/validator/src/services/attestationDuties.ts +++ b/packages/validator/src/services/attestationDuties.ts @@ -1,6 +1,5 @@ -import {toHexString} from "@chainsafe/ssz"; import {SLOTS_PER_EPOCH} from "@lodestar/params"; -import {sleep} from "@lodestar/utils"; +import {sleep, toPubkeyHex} from "@lodestar/utils"; import {computeEpochAtSlot, isAggregatorFromCommitteeLength, isStartSlotOfEpoch} from "@lodestar/state-transition"; import {BLSSignature, Epoch, Slot, ValidatorIndex, RootHex} from "@lodestar/types"; import {ApiClient, routes} from "@lodestar/api"; @@ -97,7 +96,7 @@ export class AttestationDutiesService { removeDutiesForKey(pubkey: PubkeyHex): void { for (const [epoch, attDutiesAtEpoch] of this.dutiesByIndexByEpoch) { for (const [vIndex, attDutyAndProof] of attDutiesAtEpoch.dutiesByIndex) { - if (toHexString(attDutyAndProof.duty.pubkey) === pubkey) { + if (toPubkeyHex(attDutyAndProof.duty.pubkey) === pubkey) { attDutiesAtEpoch.dutiesByIndex.delete(vIndex); if (attDutiesAtEpoch.dutiesByIndex.size === 0) { this.dutiesByIndexByEpoch.delete(epoch); @@ -244,7 +243,7 @@ export class AttestationDutiesService { const attesterDuties = res.value(); const {dependentRoot} = res.meta(); const relevantDuties = attesterDuties.filter((duty) => { - const pubkeyHex = toHexString(duty.pubkey); + const pubkeyHex = toPubkeyHex(duty.pubkey); return this.validatorStore.hasVotingPubkey(pubkeyHex) && this.validatorStore.isDoppelgangerSafe(pubkeyHex); }); diff --git a/packages/validator/src/services/block.ts b/packages/validator/src/services/block.ts index 7792ef85be95..c3acf19c1669 100644 --- a/packages/validator/src/services/block.ts +++ b/packages/validator/src/services/block.ts @@ -1,4 +1,3 @@ -import {toHexString} from "@chainsafe/ssz"; import { BLSPubkey, Slot, @@ -15,7 +14,7 @@ import { } from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; import {ForkPreBlobs, ForkBlobs, ForkSeq, ForkExecution, ForkName} from "@lodestar/params"; -import {extendError, prettyBytes, prettyWeiToEth} from "@lodestar/utils"; +import {extendError, prettyBytes, prettyWeiToEth, toPubkeyHex} from "@lodestar/utils"; import {ApiClient, routes} from "@lodestar/api"; import {IClock, LoggerVc} from "../util/index.js"; import {PubkeyHex} from "../types.js"; @@ -110,7 +109,7 @@ export class BlockProposingService { /** Produce a block at the given slot for pubkey */ private async createAndPublishBlock(pubkey: BLSPubkey, slot: Slot): Promise { - const pubkeyHex = toHexString(pubkey); + const pubkeyHex = toPubkeyHex(pubkey); const logCtx = {slot, validator: prettyBytes(pubkeyHex)}; // Wrap with try catch here to re-use `logCtx` diff --git a/packages/validator/src/services/blockDuties.ts b/packages/validator/src/services/blockDuties.ts index 3282987f5d9e..d0e16f60e816 100644 --- a/packages/validator/src/services/blockDuties.ts +++ b/packages/validator/src/services/blockDuties.ts @@ -1,8 +1,7 @@ -import {toHexString} from "@chainsafe/ssz"; import {computeEpochAtSlot, computeStartSlotAtEpoch} from "@lodestar/state-transition"; import {BLSPubkey, Epoch, RootHex, Slot} from "@lodestar/types"; import {ApiClient, routes} from "@lodestar/api"; -import {sleep} from "@lodestar/utils"; +import {sleep, toPubkeyHex} from "@lodestar/utils"; import {ChainConfig} from "@lodestar/config"; import {IClock, differenceHex, LoggerVc} from "../util/index.js"; import {PubkeyHex} from "../types.js"; @@ -67,7 +66,7 @@ export class BlockDutiesService { if (dutyAtEpoch) { for (const proposer of dutyAtEpoch.data) { if (proposer.slot === slot) { - publicKeys.set(toHexString(proposer.pubkey), proposer.pubkey); + publicKeys.set(toPubkeyHex(proposer.pubkey), proposer.pubkey); } } } @@ -78,7 +77,7 @@ export class BlockDutiesService { removeDutiesForKey(pubkey: PubkeyHex): void { for (const blockDutyAtEpoch of this.proposers.values()) { blockDutyAtEpoch.data = blockDutyAtEpoch.data.filter((proposer) => { - return toHexString(proposer.pubkey) !== pubkey; + return toPubkeyHex(proposer.pubkey) !== pubkey; }); } } @@ -187,7 +186,7 @@ export class BlockDutiesService { const proposerDuties = res.value(); const {dependentRoot} = res.meta(); const relevantDuties = proposerDuties.filter((duty) => { - const pubkeyHex = toHexString(duty.pubkey); + const pubkeyHex = toPubkeyHex(duty.pubkey); return this.validatorStore.hasVotingPubkey(pubkeyHex) && this.validatorStore.isDoppelgangerSafe(pubkeyHex); }); diff --git a/packages/validator/src/services/indices.ts b/packages/validator/src/services/indices.ts index c6ef40b473e5..ec5155322918 100644 --- a/packages/validator/src/services/indices.ts +++ b/packages/validator/src/services/indices.ts @@ -1,6 +1,5 @@ -import {toHexString} from "@chainsafe/ssz"; import {ValidatorIndex} from "@lodestar/types"; -import {Logger, MapDef} from "@lodestar/utils"; +import {Logger, MapDef, toPubkeyHex} from "@lodestar/utils"; import {ApiClient, routes} from "@lodestar/api"; import {batchItems} from "../util/index.js"; import {Metrics} from "../metrics.js"; @@ -135,7 +134,7 @@ export class IndicesService { const status = statusToSimpleStatusMapping(validator.status); allValidatorStatuses.set(status, allValidatorStatuses.getOrDefault(status) + 1); - const pubkeyHex = toHexString(validator.validator.pubkey); + const pubkeyHex = toPubkeyHex(validator.validator.pubkey); if (!this.pubkey2index.has(pubkeyHex)) { this.logger.info("Validator seen on beacon chain", { validatorIndex: validator.index, diff --git a/packages/validator/src/services/syncCommitteeDuties.ts b/packages/validator/src/services/syncCommitteeDuties.ts index dd663528f751..ea448add15ec 100644 --- a/packages/validator/src/services/syncCommitteeDuties.ts +++ b/packages/validator/src/services/syncCommitteeDuties.ts @@ -1,4 +1,3 @@ -import {toHexString} from "@chainsafe/ssz"; import {EPOCHS_PER_SYNC_COMMITTEE_PERIOD, SYNC_COMMITTEE_SUBNET_SIZE} from "@lodestar/params"; import { computeEpochAtSlot, @@ -10,6 +9,7 @@ import { import {ChainForkConfig} from "@lodestar/config"; import {BLSSignature, Epoch, Slot, SyncPeriod, ValidatorIndex} from "@lodestar/types"; import {ApiClient, routes} from "@lodestar/api"; +import {toPubkeyHex} from "@lodestar/utils"; import {IClock, LoggerVc} from "../util/index.js"; import {PubkeyHex} from "../types.js"; import {Metrics} from "../metrics.js"; @@ -287,7 +287,7 @@ export class SyncCommitteeDutiesService { // Using `alreadyWarnedReorg` avoids excessive logs. // TODO: Use memory-efficient toHexString() - const pubkeyHex = toHexString(duty.pubkey); + const pubkeyHex = toPubkeyHex(duty.pubkey); dutiesByIndex.set(validatorIndex, {duty: {pubkey: pubkeyHex, validatorIndex, subnets}}); } diff --git a/packages/validator/src/services/validatorStore.ts b/packages/validator/src/services/validatorStore.ts index fa9d855aa24a..c6130f1fab95 100644 --- a/packages/validator/src/services/validatorStore.ts +++ b/packages/validator/src/services/validatorStore.ts @@ -42,6 +42,7 @@ import { SignedAggregateAndProof, } from "@lodestar/types"; import {routes} from "@lodestar/api"; +import {toPubkeyHex} from "@lodestar/utils"; import {ISlashingProtection} from "../slashingProtection/index.js"; import {PubkeyHex} from "../types.js"; import {externalSignerPostSignature, SignableMessageType, SignableMessage} from "../util/externalSignerClient.js"; @@ -723,7 +724,7 @@ export class ValidatorStore { regAttributes: {feeRecipient: Eth1Address; gasLimit: number}, slot: Slot ): Promise { - const pubkeyHex = typeof pubkeyMaybeHex === "string" ? pubkeyMaybeHex : toHexString(pubkeyMaybeHex); + const pubkeyHex = typeof pubkeyMaybeHex === "string" ? pubkeyMaybeHex : toPubkeyHex(pubkeyMaybeHex); const {feeRecipient, gasLimit} = regAttributes; const regFullKey = `${feeRecipient}-${gasLimit}`; const validatorData = this.validators.get(pubkeyHex); @@ -748,7 +749,7 @@ export class ValidatorStore { signableMessage: SignableMessage ): Promise { // TODO: Refactor indexing to not have to run toHexString() on the pubkey every time - const pubkeyHex = typeof pubkey === "string" ? pubkey : toHexString(pubkey); + const pubkeyHex = typeof pubkey === "string" ? pubkey : toPubkeyHex(pubkey); const signer = this.validators.get(pubkeyHex)?.signer; if (!signer) { @@ -787,7 +788,7 @@ export class ValidatorStore { private getSignerAndPubkeyHex(pubkey: BLSPubkeyMaybeHex): [Signer, string] { // TODO: Refactor indexing to not have to run toHexString() on the pubkey every time - const pubkeyHex = typeof pubkey === "string" ? pubkey : toHexString(pubkey); + const pubkeyHex = typeof pubkey === "string" ? pubkey : toPubkeyHex(pubkey); const signer = this.validators.get(pubkeyHex)?.signer; if (!signer) { throw Error(`Validator pubkey ${pubkeyHex} not known`); @@ -813,7 +814,7 @@ export class ValidatorStore { } private assertDoppelgangerSafe(pubKey: PubkeyHex | BLSPubkey): void { - const pubkeyHex = typeof pubKey === "string" ? pubKey : toHexString(pubKey); + const pubkeyHex = typeof pubKey === "string" ? pubKey : toPubkeyHex(pubKey); if (!this.isDoppelgangerSafe(pubkeyHex)) { throw new Error(`Doppelganger state for key ${pubkeyHex} is not safe`); } diff --git a/packages/validator/src/slashingProtection/index.ts b/packages/validator/src/slashingProtection/index.ts index dedbccf6cf94..bc57b0e51c13 100644 --- a/packages/validator/src/slashingProtection/index.ts +++ b/packages/validator/src/slashingProtection/index.ts @@ -1,6 +1,5 @@ -import {toHexString} from "@chainsafe/ssz"; import {BLSPubkey, Epoch, Root} from "@lodestar/types"; -import {Logger} from "@lodestar/utils"; +import {Logger, toPubkeyHex} from "@lodestar/utils"; import {LodestarValidatorDatabaseController} from "../types.js"; import {uniqueVectorArr} from "../slashingProtection/utils.js"; import {BlockBySlotRepository, SlashingProtectionBlockService} from "./block/index.js"; @@ -63,7 +62,7 @@ export class SlashingProtection implements ISlashingProtection { async importInterchange(interchange: Interchange, genesisValidatorsRoot: Root, logger?: Logger): Promise { const {data} = parseInterchange(interchange, genesisValidatorsRoot); for (const validator of data) { - logger?.info("Importing slashing protection", {pubkey: toHexString(validator.pubkey)}); + logger?.info("Importing slashing protection", {pubkey: toPubkeyHex(validator.pubkey)}); await this.blockService.importBlocks(validator.pubkey, validator.signedBlocks); await this.attestationService.importAttestations(validator.pubkey, validator.signedAttestations); } @@ -77,7 +76,7 @@ export class SlashingProtection implements ISlashingProtection { ): Promise { const validatorData: InterchangeLodestar["data"] = []; for (const pubkey of pubkeys) { - logger?.info("Exporting slashing protection", {pubkey: toHexString(pubkey)}); + logger?.info("Exporting slashing protection", {pubkey: toPubkeyHex(pubkey)}); validatorData.push({ pubkey, signedBlocks: await this.blockService.exportBlocks(pubkey), diff --git a/packages/validator/src/slashingProtection/interchange/formats/completeV4.ts b/packages/validator/src/slashingProtection/interchange/formats/completeV4.ts index 26d7f44f2e83..66aa31c52194 100644 --- a/packages/validator/src/slashingProtection/interchange/formats/completeV4.ts +++ b/packages/validator/src/slashingProtection/interchange/formats/completeV4.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ import {fromHexString, toHexString} from "@chainsafe/ssz"; +import {toPubkeyHex} from "@lodestar/utils"; import {InterchangeLodestar} from "../types.js"; import {fromOptionalHexString, numToString, toOptionalHexString} from "../../utils.js"; @@ -93,7 +94,7 @@ export function serializeInterchangeCompleteV4({ genesis_validators_root: toHexString(genesisValidatorsRoot), }, data: data.map((validator) => ({ - pubkey: toHexString(validator.pubkey), + pubkey: toPubkeyHex(validator.pubkey), signed_blocks: validator.signedBlocks.map((block) => ({ slot: numToString(block.slot), signing_root: toOptionalHexString(block.signingRoot), diff --git a/packages/validator/src/slashingProtection/interchange/formats/v5.ts b/packages/validator/src/slashingProtection/interchange/formats/v5.ts index c70dc84b1ed0..1c7f67b706a5 100644 --- a/packages/validator/src/slashingProtection/interchange/formats/v5.ts +++ b/packages/validator/src/slashingProtection/interchange/formats/v5.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ import {fromHexString, toHexString} from "@chainsafe/ssz"; +import {toPubkeyHex} from "@lodestar/utils"; import {InterchangeLodestar} from "../types.js"; import {fromOptionalHexString, numToString, toOptionalHexString} from "../../utils.js"; @@ -88,7 +89,7 @@ export function serializeInterchangeV5({data, genesisValidatorsRoot}: Interchang genesis_validators_root: toHexString(genesisValidatorsRoot), }, data: data.map((validator) => ({ - pubkey: toHexString(validator.pubkey), + pubkey: toPubkeyHex(validator.pubkey), signed_blocks: validator.signedBlocks.map((block) => ({ slot: numToString(block.slot), signing_root: toOptionalHexString(block.signingRoot),