diff --git a/packages/beacon-node/package.json b/packages/beacon-node/package.json index 6ed62e5bf03f..c73c37f2f667 100644 --- a/packages/beacon-node/package.json +++ b/packages/beacon-node/package.json @@ -95,8 +95,7 @@ }, "dependencies": { "@chainsafe/as-sha256": "^0.4.1", - "@chainsafe/bls": "^8.1.0", - "@chainsafe/blst": "^1.0.0", + "@chainsafe/blst": "^1.0.1", "@chainsafe/discv5": "^9.0.0", "@chainsafe/enr": "^3.0.0", "@chainsafe/libp2p-gossipsub": "^13.0.0", diff --git a/packages/beacon-node/src/chain/bls/interface.ts b/packages/beacon-node/src/chain/bls/interface.ts index e9c98ba1920e..f37db9b34aeb 100644 --- a/packages/beacon-node/src/chain/bls/interface.ts +++ b/packages/beacon-node/src/chain/bls/interface.ts @@ -1,4 +1,4 @@ -import {PublicKey} from "@chainsafe/bls/types"; +import {PublicKey} from "@chainsafe/blst"; import {ISignatureSet} from "@lodestar/state-transition"; export type VerifySignatureOpts = { diff --git a/packages/beacon-node/src/chain/bls/jobItem.ts b/packages/beacon-node/src/chain/bls/jobItem.ts index c2a49aa61676..a67a10bf2a4d 100644 --- a/packages/beacon-node/src/chain/bls/jobItem.ts +++ b/packages/beacon-node/src/chain/bls/jobItem.ts @@ -1,12 +1,11 @@ -import bls from "@chainsafe/bls"; -import {CoordType, PointFormat, PublicKey} from "@chainsafe/bls/types"; +import {PublicKey, aggregatePublicKeys, aggregateSignatures, randomBytesNonZero} from "@chainsafe/blst"; +import {signatureFromBytes} from "@lodestar/utils"; import {ISignatureSet, SignatureSetType} from "@lodestar/state-transition"; import {LinkedList} from "../../util/array.js"; import {Metrics} from "../../metrics/metrics.js"; import {VerifySignatureOpts} from "./interface.js"; import {getAggregatedPubkey} from "./utils.js"; import {BlsWorkReq} from "./types.js"; -import {randomBytesNonZero} from "./utils.js"; export type JobQueueItem = JobQueueItemDefault | JobQueueItemSameMessage; @@ -50,7 +49,7 @@ export function jobItemSigSets(job: JobQueueItem): number { * Prepare BlsWorkReq from JobQueueItem * WARNING: May throw with untrusted user input */ -export function jobItemWorkReq(job: JobQueueItem, format: PointFormat, metrics: Metrics | null): BlsWorkReq { +export function jobItemWorkReq(job: JobQueueItem, metrics: Metrics | null): BlsWorkReq { switch (job.type) { case JobQueueItemType.default: return { @@ -71,11 +70,9 @@ export function jobItemWorkReq(job: JobQueueItem, format: PointFormat, metrics: // and not a problem in the near future // this is monitored on v1.11.0 https://github.com/ChainSafe/lodestar/pull/5912#issuecomment-1700320307 const timer = metrics?.blsThreadPool.signatureDeserializationMainThreadDuration.startTimer(); - const signatures = job.sets.map((set) => bls.Signature.fromBytes(set.signature, CoordType.affine, true)); + const signatures = job.sets.map((set) => signatureFromBytes(set.signature)); timer?.(); - // adding verification randomness is napi specific. must not attempt with herumi until - // @chainsafe/bls is updated to support it with herumi const randomness: Uint8Array[] = []; for (let i = 0; i < job.sets.length; i++) { randomness.push(randomBytesNonZero(8)); @@ -85,8 +82,8 @@ export function jobItemWorkReq(job: JobQueueItem, format: PointFormat, metrics: sets: [ { message: job.message, - publicKey: bls.PublicKey.aggregate(job.sets.map((set, i) => set.publicKey.multiplyBy(randomness[i]))), - signature: bls.Signature.aggregate(signatures.map((sig, i) => sig.multiplyBy(randomness[i]))), + publicKey: aggregatePublicKeys(job.sets.map((set, i) => set.publicKey.multiplyBy(randomness[i]))), + signature: aggregateSignatures(signatures.map((sig, i) => sig.multiplyBy(randomness[i]))), }, ], }; diff --git a/packages/beacon-node/src/chain/bls/multiThread.ts b/packages/beacon-node/src/chain/bls/multiThread.ts index bdc80c18818c..67820d84e874 100644 --- a/packages/beacon-node/src/chain/bls/multiThread.ts +++ b/packages/beacon-node/src/chain/bls/multiThread.ts @@ -1,6 +1,5 @@ import os from "node:os"; -import bls from "@chainsafe/bls"; -import {PointFormat, PublicKey} from "@chainsafe/bls/types"; +import {PublicKey} from "@chainsafe/blst"; import {Logger} from "@lodestar/utils"; import {ISignatureSet} from "@lodestar/state-transition"; import {QueueError, QueueErrorCode} from "../../util/queue/index.js"; @@ -76,8 +75,6 @@ export class BlsMultiThreadWorkerPool implements IBlsVerifier { private readonly logger: Logger; private readonly metrics: Metrics | null; - private readonly format = PointFormat.uncompressed; - private blsPoolSize: number; private workersBusy = 0; @@ -93,10 +90,6 @@ export class BlsMultiThreadWorkerPool implements IBlsVerifier { private closed = false; constructor(options: BlsMultiThreadWorkerPoolOptions, modules: BlsMultiThreadWorkerPoolModules) { - if (bls.implementation === "herumi") { - throw new Error("Herumi BLS implementation is not supported"); - } - this.logger = modules.logger; this.blsVerifyAllMultiThread = options.blsVerifyAllMultiThread ?? false; @@ -298,7 +291,7 @@ export class BlsMultiThreadWorkerPool implements IBlsVerifier { try { // Note: This can throw, must be handled per-job. // Pubkey and signature aggregation is defered here - workReq = jobItemWorkReq(job, this.format, this.metrics); + workReq = jobItemWorkReq(job, this.metrics); } catch (e) { this.metrics?.blsThreadPool.errorAggregateSignatureSetsCount.inc({type: job.type}); diff --git a/packages/beacon-node/src/chain/bls/singleThread.ts b/packages/beacon-node/src/chain/bls/singleThread.ts index 9e6005fea105..7c80f0da89f9 100644 --- a/packages/beacon-node/src/chain/bls/singleThread.ts +++ b/packages/beacon-node/src/chain/bls/singleThread.ts @@ -1,7 +1,6 @@ -import {PublicKey, Signature} from "@chainsafe/bls/types"; -import bls from "@chainsafe/bls"; -import {CoordType} from "@chainsafe/bls/types"; +import {PublicKey, Signature, aggregatePublicKeys, aggregateSignatures, verify} from "@chainsafe/blst"; import {ISignatureSet} from "@lodestar/state-transition"; +import {signatureFromBytes} from "@lodestar/utils"; import {Metrics} from "../../metrics/index.js"; import {IBlsVerifier} from "./interface.js"; import {verifySets} from "./verifySets.js"; @@ -40,12 +39,12 @@ export class BlsSingleThreadVerifier implements IBlsVerifier { message: Uint8Array ): Promise { const timer = this.metrics?.blsThreadPool.mainThreadDurationInThreadPool.startTimer(); - const pubkey = bls.PublicKey.aggregate(sets.map((set) => set.publicKey)); + const pubkey = aggregatePublicKeys(sets.map((set) => set.publicKey)); let isAllValid = true; // validate signature = true const signatures = sets.map((set) => { try { - return bls.Signature.fromBytes(set.signature, CoordType.affine, true); + return signatureFromBytes(set.signature); } catch (_) { // at least one set has malformed signature isAllValid = false; @@ -54,8 +53,8 @@ export class BlsSingleThreadVerifier implements IBlsVerifier { }); if (isAllValid) { - const signature = bls.Signature.aggregate(signatures as Signature[]); - isAllValid = signature.verify(pubkey, message); + const signature = aggregateSignatures(signatures as Signature[]); + isAllValid = verify(message, pubkey, signature); } let result: boolean[]; @@ -67,7 +66,7 @@ export class BlsSingleThreadVerifier implements IBlsVerifier { if (sig === null) { return false; } - return sig.verify(set.publicKey, message); + return verify(message, set.publicKey, sig); }); } diff --git a/packages/beacon-node/src/chain/bls/types.ts b/packages/beacon-node/src/chain/bls/types.ts index 35ca894ae43e..3797f7df7385 100644 --- a/packages/beacon-node/src/chain/bls/types.ts +++ b/packages/beacon-node/src/chain/bls/types.ts @@ -1,4 +1,4 @@ -import {PublicKey, Signature} from "@chainsafe/bls/types"; +import {PublicKey, Signature} from "@chainsafe/blst"; import {VerifySignatureOpts} from "./interface.js"; export type DeserializedKeySet = { diff --git a/packages/beacon-node/src/chain/bls/utils.ts b/packages/beacon-node/src/chain/bls/utils.ts index 08ef243bb598..0c422709ec56 100644 --- a/packages/beacon-node/src/chain/bls/utils.ts +++ b/packages/beacon-node/src/chain/bls/utils.ts @@ -1,5 +1,4 @@ -import bls from "@chainsafe/bls"; -import {PublicKey} from "@chainsafe/bls/types"; +import {PublicKey, aggregatePublicKeys} from "@chainsafe/blst"; import {ISignatureSet, SignatureSetType} from "@lodestar/state-transition"; import {Metrics} from "../../metrics/metrics.js"; import {WorkResultError} from "./types.js"; @@ -11,7 +10,7 @@ export function getAggregatedPubkey(signatureSet: ISignatureSet, metrics: Metric case SignatureSetType.aggregate: { const timer = metrics?.blsThreadPool.pubkeysAggregationMainThreadDuration.startTimer(); - const pubkeys = bls.PublicKey.aggregate(signatureSet.pubkeys); + const pubkeys = aggregatePublicKeys(signatureSet.pubkeys); timer?.(); return pubkeys; } @@ -51,19 +50,6 @@ export function chunkifyMaximizeChunkSize(arr: T[], minPerChunk: number): T[] return arrArr; } -/** - * `rand` must not be exactly zero. Otherwise it would allow the verification of invalid signatures - * See https://github.com/ChainSafe/blst-ts/issues/45 - */ -export function randomBytesNonZero(bytesCount: number): Uint8Array { - const rand = crypto.getRandomValues(new Uint8Array(bytesCount)); - for (let i = 0; i < bytesCount; i++) { - if (rand[i] !== 0) return rand; - } - rand[0] = 1; - return rand; -} - export function getJobResultError(jobResult: WorkResultError | null, i: number): Error { const workerError = jobResult ? Error(jobResult.error.message) : Error(`No jobResult for index ${i}`); if (jobResult?.error?.stack) workerError.stack = jobResult.error.stack; diff --git a/packages/beacon-node/src/chain/bls/verifySets.ts b/packages/beacon-node/src/chain/bls/verifySets.ts index ebd8bbb44e34..d54d0bac5cf0 100644 --- a/packages/beacon-node/src/chain/bls/verifySets.ts +++ b/packages/beacon-node/src/chain/bls/verifySets.ts @@ -1,4 +1,9 @@ -import bls from "@chainsafe/bls"; +import { + asyncVerify, + asyncVerifyMultipleAggregateSignatures, + verify, + verifyMultipleAggregateSignatures, +} from "@chainsafe/blst"; import {WorkRequestSet} from "./types.js"; const MIN_SET_COUNT_TO_BATCH = 2; @@ -10,7 +15,7 @@ const MIN_SET_COUNT_TO_BATCH = 2; export function verifySets(sets: WorkRequestSet[]): boolean { try { if (sets.length >= MIN_SET_COUNT_TO_BATCH) { - return bls.verifyMultipleSignatures(sets); + return verifyMultipleAggregateSignatures(sets); } // .every on an empty array returns true @@ -19,7 +24,7 @@ export function verifySets(sets: WorkRequestSet[]): boolean { } // If too few signature sets verify them without batching - return sets.every(({message, publicKey, signature}) => bls.verify(publicKey, message, signature)); + return sets.every(({message, publicKey, signature}) => verify(message, publicKey, signature)); } catch (_) { // A signature could be malformed, in that case fromBytes throws error // blst-ts `verifyMultipleSignatures` is also a fallible operation if mul_n_aggregate fails @@ -31,7 +36,7 @@ export function verifySets(sets: WorkRequestSet[]): boolean { export async function asyncVerifySets(sets: WorkRequestSet[]): Promise { try { if (sets.length >= MIN_SET_COUNT_TO_BATCH) { - return await bls.asyncVerifyMultipleSignatures(sets); + return await asyncVerifyMultipleAggregateSignatures(sets); } // .every on an empty array returns true @@ -40,7 +45,7 @@ export async function asyncVerifySets(sets: WorkRequestSet[]): Promise } const promises = await Promise.all( - sets.map(({message, publicKey, signature}) => bls.asyncVerify(publicKey, message, signature)) + sets.map(({message, publicKey, signature}) => asyncVerify(message, publicKey, signature)) ); // If too few signature sets verify them without batching return promises.every((isValid) => isValid); diff --git a/packages/beacon-node/src/chain/chain.ts b/packages/beacon-node/src/chain/chain.ts index 047f7741c2f2..31171101747f 100644 --- a/packages/beacon-node/src/chain/chain.ts +++ b/packages/beacon-node/src/chain/chain.ts @@ -585,7 +585,7 @@ export class BeaconChain implements IBeaconChain { RegenCaller.produceBlock ); const proposerIndex = state.epochCtx.getBeaconProposer(slot); - const proposerPubKey = state.epochCtx.index2pubkey[proposerIndex].toBytes(); + const proposerPubKey = state.epochCtx.index2pubkey[proposerIndex].serialize(); const {body, blobs, executionPayloadValue, shouldOverrideBuilder} = await produceBlockBody.call( this, diff --git a/packages/beacon-node/src/chain/opPools/aggregatedAttestationPool.ts b/packages/beacon-node/src/chain/opPools/aggregatedAttestationPool.ts index c94e5d81e823..a2bb861529ad 100644 --- a/packages/beacon-node/src/chain/opPools/aggregatedAttestationPool.ts +++ b/packages/beacon-node/src/chain/opPools/aggregatedAttestationPool.ts @@ -1,5 +1,5 @@ -import bls from "@chainsafe/bls"; import {toHexString} from "@chainsafe/ssz"; +import {aggregateSignatures} from "@chainsafe/blst"; import {ForkName, ForkSeq, MAX_ATTESTATIONS, MIN_ATTESTATION_INCLUSION_DELAY, SLOTS_PER_EPOCH} from "@lodestar/params"; import {phase0, Epoch, Slot, ssz, ValidatorIndex, RootHex} from "@lodestar/types"; import { @@ -11,9 +11,9 @@ import { getBlockRootAtSlot, } from "@lodestar/state-transition"; import {IForkChoice, EpochDifference} from "@lodestar/fork-choice"; -import {toHex, MapDef} from "@lodestar/utils"; +import {toHex, MapDef, signatureFromBytesNoCheck} from "@lodestar/utils"; import {intersectUint8Arrays, IntersectResult} from "../../util/bitArray.js"; -import {pruneBySlot, signatureFromBytesNoCheck} from "./utils.js"; +import {pruneBySlot} from "./utils.js"; import {InsertOutcome} from "./types.js"; type DataRootHex = string; @@ -383,7 +383,7 @@ export function aggregateInto(attestation1: AttestationWithIndex, attestation2: const signature1 = signatureFromBytesNoCheck(attestation1.attestation.signature); const signature2 = signatureFromBytesNoCheck(attestation2.attestation.signature); - attestation1.attestation.signature = bls.Signature.aggregate([signature1, signature2]).toBytes(); + attestation1.attestation.signature = aggregateSignatures([signature1, signature2]).serialize(); } /** diff --git a/packages/beacon-node/src/chain/opPools/attestationPool.ts b/packages/beacon-node/src/chain/opPools/attestationPool.ts index 804d8798cbc2..9bab4c5a9c88 100644 --- a/packages/beacon-node/src/chain/opPools/attestationPool.ts +++ b/packages/beacon-node/src/chain/opPools/attestationPool.ts @@ -1,11 +1,10 @@ -import {PointFormat, Signature} from "@chainsafe/bls/types"; -import bls from "@chainsafe/bls"; +import {Signature, aggregateSignatures} from "@chainsafe/blst"; import {BitArray} from "@chainsafe/ssz"; import {phase0, Slot, RootHex} from "@lodestar/types"; -import {MapDef} from "@lodestar/utils"; +import {MapDef, signatureFromBytesNoCheck} from "@lodestar/utils"; import {IClock} from "../../util/clock.js"; import {InsertOutcome, OpPoolError, OpPoolErrorCode} from "./types.js"; -import {pruneBySlot, signatureFromBytesNoCheck} from "./utils.js"; +import {pruneBySlot} from "./utils.js"; /** * The number of slots that will be stored in the pool. @@ -191,10 +190,7 @@ function aggregateAttestationInto(aggregate: AggregateFast, attestation: phase0. } aggregate.aggregationBits.set(bitIndex, true); - aggregate.signature = bls.Signature.aggregate([ - aggregate.signature, - signatureFromBytesNoCheck(attestation.signature), - ]); + aggregate.signature = aggregateSignatures([aggregate.signature, signatureFromBytesNoCheck(attestation.signature)]); return InsertOutcome.Aggregated; } @@ -217,6 +213,6 @@ function fastToAttestation(aggFast: AggregateFast): phase0.Attestation { return { data: aggFast.data, aggregationBits: aggFast.aggregationBits, - signature: aggFast.signature.toBytes(PointFormat.compressed), + signature: aggFast.signature.serialize(true), }; } diff --git a/packages/beacon-node/src/chain/opPools/syncCommitteeMessagePool.ts b/packages/beacon-node/src/chain/opPools/syncCommitteeMessagePool.ts index 03552992a72a..db04272e5da5 100644 --- a/packages/beacon-node/src/chain/opPools/syncCommitteeMessagePool.ts +++ b/packages/beacon-node/src/chain/opPools/syncCommitteeMessagePool.ts @@ -1,12 +1,11 @@ -import {PointFormat, Signature} from "@chainsafe/bls/types"; -import bls from "@chainsafe/bls"; +import {Signature, aggregateSignatures} from "@chainsafe/blst"; import {BitArray, toHexString} from "@chainsafe/ssz"; import {SYNC_COMMITTEE_SIZE, SYNC_COMMITTEE_SUBNET_COUNT} from "@lodestar/params"; import {altair, Root, Slot, SubcommitteeIndex} from "@lodestar/types"; -import {MapDef} from "@lodestar/utils"; +import {MapDef, signatureFromBytesNoCheck} from "@lodestar/utils"; import {IClock} from "../../util/clock.js"; import {InsertOutcome, OpPoolError, OpPoolErrorCode} from "./types.js"; -import {pruneBySlot, signatureFromBytesNoCheck} from "./utils.js"; +import {pruneBySlot} from "./utils.js"; /** * SyncCommittee signatures are only useful during a single slot according to our peer's clocks @@ -108,7 +107,7 @@ export class SyncCommitteeMessagePool { return { ...contribution, aggregationBits: contribution.aggregationBits, - signature: contribution.signature.toBytes(PointFormat.compressed), + signature: contribution.signature.serialize(true), }; } @@ -136,7 +135,7 @@ function aggregateSignatureInto( } contribution.aggregationBits.set(indexInSubcommittee, true); - contribution.signature = bls.Signature.aggregate([ + contribution.signature = aggregateSignatures([ contribution.signature, signatureFromBytesNoCheck(signature.signature), ]); diff --git a/packages/beacon-node/src/chain/opPools/syncContributionAndProofPool.ts b/packages/beacon-node/src/chain/opPools/syncContributionAndProofPool.ts index 51b433fd6e50..ba77de142f9a 100644 --- a/packages/beacon-node/src/chain/opPools/syncContributionAndProofPool.ts +++ b/packages/beacon-node/src/chain/opPools/syncContributionAndProofPool.ts @@ -1,12 +1,11 @@ -import type {Signature} from "@chainsafe/bls/types"; -import bls from "@chainsafe/bls"; +import {Signature, aggregateSignatures} from "@chainsafe/blst"; import {BitArray, toHexString} from "@chainsafe/ssz"; import {SYNC_COMMITTEE_SIZE, SYNC_COMMITTEE_SUBNET_SIZE} from "@lodestar/params"; import {altair, Slot, Root, ssz} from "@lodestar/types"; import {G2_POINT_AT_INFINITY} from "@lodestar/state-transition"; -import {MapDef} from "@lodestar/utils"; +import {MapDef, signatureFromBytesNoCheck} from "@lodestar/utils"; import {InsertOutcome, OpPoolError, OpPoolErrorCode} from "./types.js"; -import {pruneBySlot, signatureFromBytesNoCheck} from "./utils.js"; +import {pruneBySlot} from "./utils.js"; /** * SyncCommittee aggregates are only useful for the next block they have signed. @@ -182,6 +181,6 @@ export function aggregate(bestContributionBySubnet: Map, slot: Slot, slotsRetained: return lowestPermissibleSlot; } -/** - * De-serialize bytes into Signature. - * No need to verify Signature is valid, already run sig-verify = false - */ -export function signatureFromBytesNoCheck(signature: Uint8Array): Signature { - return bls.Signature.fromBytes(signature, CoordType.affine, false); -} - /** * Ensures that a SignedBLSToExecutionChange object is _still_ valid for block inclusion. An object valid for the pool, * can become invalid for certain forks. diff --git a/packages/beacon-node/src/chain/validation/signatureSets/aggregateAndProof.ts b/packages/beacon-node/src/chain/validation/signatureSets/aggregateAndProof.ts index 2bc2e62c861f..59787341cfb9 100644 --- a/packages/beacon-node/src/chain/validation/signatureSets/aggregateAndProof.ts +++ b/packages/beacon-node/src/chain/validation/signatureSets/aggregateAndProof.ts @@ -1,4 +1,4 @@ -import type {PublicKey} from "@chainsafe/bls/types"; +import {PublicKey} from "@chainsafe/blst"; import {DOMAIN_AGGREGATE_AND_PROOF} from "@lodestar/params"; import {ssz} from "@lodestar/types"; import {Epoch, phase0} from "@lodestar/types"; diff --git a/packages/beacon-node/src/chain/validation/signatureSets/selectionProof.ts b/packages/beacon-node/src/chain/validation/signatureSets/selectionProof.ts index 09e0a5ef12be..5e129a88aa20 100644 --- a/packages/beacon-node/src/chain/validation/signatureSets/selectionProof.ts +++ b/packages/beacon-node/src/chain/validation/signatureSets/selectionProof.ts @@ -1,4 +1,4 @@ -import type {PublicKey} from "@chainsafe/bls/types"; +import {PublicKey} from "@chainsafe/blst"; import {DOMAIN_SELECTION_PROOF} from "@lodestar/params"; import {phase0, Slot, ssz} from "@lodestar/types"; import {computeSigningRoot, createSingleSignatureSetFromComponents, ISignatureSet} from "@lodestar/state-transition"; diff --git a/packages/beacon-node/src/chain/validation/signatureSets/syncCommitteeContribution.ts b/packages/beacon-node/src/chain/validation/signatureSets/syncCommitteeContribution.ts index 71b7970716e9..0e1eaefef7c6 100644 --- a/packages/beacon-node/src/chain/validation/signatureSets/syncCommitteeContribution.ts +++ b/packages/beacon-node/src/chain/validation/signatureSets/syncCommitteeContribution.ts @@ -1,4 +1,4 @@ -import type {PublicKey} from "@chainsafe/bls/types"; +import {PublicKey} from "@chainsafe/blst"; import {altair, ssz} from "@lodestar/types"; import {DOMAIN_SYNC_COMMITTEE} from "@lodestar/params"; import {CachedBeaconStateAltair, computeSigningRoot, ISignatureSet, SignatureSetType} from "@lodestar/state-transition"; diff --git a/packages/beacon-node/src/node/utils/interop/deposits.ts b/packages/beacon-node/src/node/utils/interop/deposits.ts index 6cce7e883b84..7a440c279cc2 100644 --- a/packages/beacon-node/src/node/utils/interop/deposits.ts +++ b/packages/beacon-node/src/node/utils/interop/deposits.ts @@ -26,7 +26,7 @@ export function interopDeposits( const withdrawalCredentialsPrefix = withEth1Credentials ? ETH1_ADDRESS_WITHDRAWAL_PREFIX : BLS_WITHDRAWAL_PREFIX; return interopSecretKeys(validatorCount).map((secretKey, i) => { - const pubkey = secretKey.toPublicKey().toBytes(); + const pubkey = secretKey.toPublicKey().serialize(); // create DepositData const withdrawalCredentials = digest(pubkey); @@ -40,7 +40,7 @@ export function interopDeposits( const domain = computeDomain(DOMAIN_DEPOSIT, config.GENESIS_FORK_VERSION, ZERO_HASH); const signingRoot = computeSigningRoot(ssz.phase0.DepositMessage, data, domain); - data.signature = secretKey.sign(signingRoot).toBytes(); + data.signature = secretKey.sign(signingRoot).serialize(); // Add to merkle tree depositDataRootList.push(ssz.phase0.DepositData.hashTreeRoot(data)); diff --git a/packages/beacon-node/test/e2e/api/impl/lightclient/endpoint.test.ts b/packages/beacon-node/test/e2e/api/impl/lightclient/endpoint.test.ts index 8c83667d5ca5..9d142153ed93 100644 --- a/packages/beacon-node/test/e2e/api/impl/lightclient/endpoint.test.ts +++ b/packages/beacon-node/test/e2e/api/impl/lightclient/endpoint.test.ts @@ -1,5 +1,5 @@ import {describe, it, beforeEach, afterEach, expect} from "vitest"; -import bls from "@chainsafe/bls"; +import {aggregatePublicKeys} from "@chainsafe/blst"; import {createBeaconConfig, ChainConfig} from "@lodestar/config"; import {chainConfig as chainConfigDef} from "@lodestar/config/default"; import {ApiError, getClient, routes} from "@lodestar/api"; @@ -131,7 +131,7 @@ describe("lightclient api", function () { const committeePubkeys = Array.from({length: SYNC_COMMITTEE_SIZE}, (_, i) => i % 2 === 0 ? pubkeys[0] : pubkeys[1] ); - const aggregatePubkey = bls.aggregatePublicKeys(committeePubkeys); + const aggregatePubkey = aggregatePublicKeys(committeePubkeys).serialize(); // single committe hash since we requested for the first period expect(committeeRes.response.data).toEqual([ ssz.altair.SyncCommittee.hashTreeRoot({ diff --git a/packages/beacon-node/test/e2e/chain/bls/multithread.test.ts b/packages/beacon-node/test/e2e/chain/bls/multithread.test.ts index ceadb4ae4c70..c13120a9a209 100644 --- a/packages/beacon-node/test/e2e/chain/bls/multithread.test.ts +++ b/packages/beacon-node/test/e2e/chain/bls/multithread.test.ts @@ -1,6 +1,5 @@ import {describe, it, beforeAll, expect, beforeEach, afterEach} from "vitest"; -import bls from "@chainsafe/bls"; -import {PublicKey} from "@chainsafe/bls/types"; +import {PublicKey, SecretKey} from "@chainsafe/blst"; import {ISignatureSet, SignatureSetType} from "@lodestar/state-transition"; import {BlsMultiThreadWorkerPool} from "../../../../src/chain/bls/index.js"; import {testLogger} from "../../../utils/logger.js"; @@ -29,7 +28,7 @@ describe("chain / bls / multithread queue", function () { beforeAll(() => { for (let i = 0; i < 3; i++) { - const sk = bls.SecretKey.fromBytes(Buffer.alloc(32, i + 1)); + const sk = SecretKey.deserialize(Buffer.alloc(32, i + 1)); const msg = Buffer.alloc(32, i + 1); const pk = sk.toPublicKey(); const sig = sk.sign(msg); @@ -37,11 +36,11 @@ describe("chain / bls / multithread queue", function () { type: SignatureSetType.single, pubkey: pk, signingRoot: msg, - signature: sig.toBytes(), + signature: sig.serialize(), }); sameMessageSets.push({ publicKey: pk, - signature: sk.sign(sameMessage).toBytes(), + signature: sk.sign(sameMessage).serialize(), }); } }); diff --git a/packages/beacon-node/test/fixtures/capella.ts b/packages/beacon-node/test/fixtures/capella.ts index fe9b0206efb1..580b57cad533 100644 --- a/packages/beacon-node/test/fixtures/capella.ts +++ b/packages/beacon-node/test/fixtures/capella.ts @@ -10,7 +10,7 @@ export function generateBlsToExecutionChanges( for (const validatorIndex of state.epochCtx.proposers) { result.push({ message: { - fromBlsPubkey: state.epochCtx.index2pubkey[validatorIndex].toBytes(), + fromBlsPubkey: state.epochCtx.index2pubkey[validatorIndex].serialize(), toExecutionAddress: Buffer.alloc(20), validatorIndex, }, diff --git a/packages/beacon-node/test/mocks/mockedBls.ts b/packages/beacon-node/test/mocks/mockedBls.ts index 0ecec5f13bde..bc6bbf361de5 100644 --- a/packages/beacon-node/test/mocks/mockedBls.ts +++ b/packages/beacon-node/test/mocks/mockedBls.ts @@ -1,4 +1,4 @@ -import {PublicKey} from "@chainsafe/bls/types"; +import {PublicKey} from "@chainsafe/blst"; import {IBlsVerifier} from "../../src/chain/bls/index.js"; export class BlsVerifierMock implements IBlsVerifier { diff --git a/packages/beacon-node/test/perf/api/impl/validator/attester.test.ts b/packages/beacon-node/test/perf/api/impl/validator/attester.test.ts index 10527fdf94b5..07bd009c81ed 100644 --- a/packages/beacon-node/test/perf/api/impl/validator/attester.test.ts +++ b/packages/beacon-node/test/perf/api/impl/validator/attester.test.ts @@ -1,5 +1,4 @@ import {itBench} from "@dapplion/benchmark"; -import {PointFormat} from "@chainsafe/bls/types"; // eslint-disable-next-line import/no-relative-packages import {generatePerfTestCachedStatePhase0, numValidators} from "../../../../../../state-transition/test/perf/util.js"; import {getPubkeysForIndices} from "../../../../../src/api/impl/validator/utils.js"; @@ -36,7 +35,7 @@ describe("api / impl / validator", () => { fn: () => { for (let i = 0; i < reqCount; i++) { const pubkey = state.epochCtx.index2pubkey[i]; - pubkey.toBytes(PointFormat.compressed); + pubkey.serialize(); } }, }); diff --git a/packages/beacon-node/test/perf/bls/bls.test.ts b/packages/beacon-node/test/perf/bls/bls.test.ts index a982cc55e499..07f61d89bc5b 100644 --- a/packages/beacon-node/test/perf/bls/bls.test.ts +++ b/packages/beacon-node/test/perf/bls/bls.test.ts @@ -1,7 +1,16 @@ import crypto from "node:crypto"; import {itBench} from "@dapplion/benchmark"; -import bls from "@chainsafe/bls"; -import {CoordType, type PublicKey, type SecretKey} from "@chainsafe/bls/types"; +import { + CoordType, + PublicKey, + SecretKey, + Signature, + aggregatePublicKeys, + aggregateSignatures, + verify, + verifyMultipleAggregateSignatures, +} from "@chainsafe/blst"; +import {signatureFromBytes} from "@lodestar/utils"; import {linspace} from "../../../src/util/numpy.js"; describe("BLS ops", function () { @@ -20,7 +29,7 @@ describe("BLS ops", function () { const bytes = new Uint8Array(32); const dataView = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); dataView.setUint32(0, i + 1, true); - const secretKey = bls.SecretKey.fromBytes(bytes); + const secretKey = SecretKey.deserialize(bytes); const publicKey = secretKey.toPublicKey(); keypair = {secretKey, publicKey}; keypairs.set(i, keypair); @@ -33,7 +42,7 @@ describe("BLS ops", function () { if (!set) { const {secretKey, publicKey} = getKeypair(i); const message = Buffer.alloc(32, i + 1); - set = {publicKey, message: message, signature: secretKey.sign(message).toBytes()}; + set = {publicKey, message: message, signature: secretKey.sign(message).serialize()}; sets.set(i, set); } return set; @@ -46,15 +55,15 @@ describe("BLS ops", function () { let set = sameMessageSets.get(i); if (!set) { const {secretKey, publicKey} = getKeypair(i); - set = {publicKey, message, signature: secretKey.sign(message).toBytes()}; + set = {publicKey, message, signature: secretKey.sign(message).serialize()}; sameMessageSets.set(i, set); } return set; } // Note: getSet() caches the value, does not re-compute every time - itBench({id: `BLS verify - ${bls.implementation}`, beforeEach: () => getSet(0)}, (set) => { - const isValid = bls.Signature.fromBytes(set.signature).verify(set.publicKey, set.message); + itBench({id: "BLS verify - blst-native", beforeEach: () => getSet(0)}, (set) => { + const isValid = verify(set.message, set.publicKey, Signature.deserialize(set.signature)); if (!isValid) throw Error("Invalid"); }); @@ -62,14 +71,14 @@ describe("BLS ops", function () { // We may want to bundle up to 32 sets in a single batch. for (const count of [3, 8, 32, 64, 128]) { itBench({ - id: `BLS verifyMultipleSignatures ${count} - ${bls.implementation}`, + id: `BLS verifyMultipleSignatures ${count} - blst-native`, beforeEach: () => linspace(0, count - 1).map((i) => getSet(i)), fn: (sets) => { - const isValid = bls.Signature.verifyMultipleSignatures( + const isValid = verifyMultipleAggregateSignatures( sets.map((set) => ({ publicKey: set.publicKey, message: set.message, - signature: bls.Signature.fromBytes(set.signature), + signature: Signature.deserialize(set.signature), })) ); if (!isValid) throw Error("Invalid"); @@ -85,8 +94,7 @@ describe("BLS ops", function () { id: `BLS deserializing ${numValidators} signatures`, fn: () => { for (const signature of signatures) { - // true = validate signature - bls.Signature.fromBytes(signature, CoordType.affine, true); + signatureFromBytes(signature); } }, }); @@ -97,15 +105,15 @@ describe("BLS ops", function () { // TODO: figure out why it does not work with 256 or more for (const count of [3, 8, 32, 64, 128]) { itBench({ - id: `BLS verifyMultipleSignatures - same message - ${count} - ${bls.implementation}`, + id: `BLS verifyMultipleSignatures - same message - ${count} - blst-native`, beforeEach: () => linspace(0, count - 1).map((i) => getSetSameMessage(i)), fn: (sets) => { // aggregate and verify aggregated signatures - const aggregatedPubkey = bls.PublicKey.aggregate(sets.map((set) => set.publicKey)); - const aggregatedSignature = bls.Signature.aggregate( - sets.map((set) => bls.Signature.fromBytes(set.signature, CoordType.affine, false)) + const aggregatedPubkey = aggregatePublicKeys(sets.map((set) => set.publicKey)); + const aggregatedSignature = aggregateSignatures( + sets.map((set) => Signature.deserialize(set.signature, CoordType.affine)) ); - const isValid = aggregatedSignature.verify(aggregatedPubkey, sets[0].message); + const isValid = verify(sets[0].message, aggregatedPubkey, aggregatedSignature); if (!isValid) throw Error("Invalid"); }, }); @@ -114,10 +122,10 @@ describe("BLS ops", function () { // Attestations in Mainnet contain 128 max on average for (const count of [32, 128]) { itBench({ - id: `BLS aggregatePubkeys ${count} - ${bls.implementation}`, + id: `BLS aggregatePubkeys ${count} - blst-native`, beforeEach: () => linspace(0, count - 1).map((i) => getKeypair(i).publicKey), fn: (pubkeys) => { - bls.PublicKey.aggregate(pubkeys); + aggregatePublicKeys(pubkeys); }, }); } diff --git a/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts b/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts index 96dda3acaece..e03fb161b5eb 100644 --- a/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts +++ b/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts @@ -64,7 +64,7 @@ describe("produceBlockBody", () => { beforeEach: async () => { const head = chain.forkChoice.getHead(); const proposerIndex = state.epochCtx.getBeaconProposer(state.slot); - const proposerPubKey = state.epochCtx.index2pubkey[proposerIndex].toBytes(); + const proposerPubKey = state.epochCtx.index2pubkey[proposerIndex].serialize(); return {chain, state, head, proposerIndex, proposerPubKey}; }, diff --git a/packages/beacon-node/test/spec/bls/bls.ts b/packages/beacon-node/test/spec/bls/bls.ts index c8d42ad84c5f..200dd0286e91 100644 --- a/packages/beacon-node/test/spec/bls/bls.ts +++ b/packages/beacon-node/test/spec/bls/bls.ts @@ -1,7 +1,16 @@ -import bls from "@chainsafe/bls"; -import {CoordType} from "@chainsafe/bls/types"; +import { + CoordType, + PublicKey, + SecretKey, + Signature, + aggregateSignatures, + aggregateVerify, + fastAggregateVerify, + verifyMultipleAggregateSignatures, + verify as _verify, +} from "@chainsafe/blst"; import {fromHexString} from "@chainsafe/ssz"; -import {toHexString} from "@lodestar/utils"; +import {signatureFromBytes, toHexString} from "@lodestar/utils"; /* eslint-disable @typescript-eslint/naming-convention */ @@ -31,7 +40,11 @@ export const testFnByType: Record any)> = { */ function aggregate_verify(input: {pubkeys: string[]; messages: string[]; signature: string}): boolean { const {pubkeys, messages, signature} = input; - return bls.verifyMultiple(pubkeys.map(fromHexString), messages.map(fromHexString), fromHexString(signature)); + try { + return aggregateVerify(messages.map(fromHexString), pubkeys.map(fromHexString), fromHexString(signature)); + } catch { + return false; + } } /** @@ -41,8 +54,8 @@ function aggregate_verify(input: {pubkeys: string[]; messages: string[]; signatu * ``` */ function aggregate(input: string[]): string { - const pks = input.map((pkHex) => bls.Signature.fromHex(pkHex)); - const agg = bls.Signature.aggregate(pks); + const pks = input.map((pkHex) => Signature.deserialize(fromHexString(pkHex))); + const agg = aggregateSignatures(pks); return agg.toHex(); } @@ -58,9 +71,15 @@ function aggregate(input: string[]): string { function fast_aggregate_verify(input: {pubkeys: string[]; message: string; signature: string}): boolean | null { const {pubkeys, message, signature} = input; try { - return bls.Signature.fromBytes(fromHexString(signature), undefined, true).verifyAggregate( - pubkeys.map((hex) => bls.PublicKey.fromBytes(fromHexString(hex), CoordType.jacobian, true)), - fromHexString(message) + const sig = signatureFromBytes(fromHexString(signature)); + return fastAggregateVerify( + fromHexString(message), + pubkeys.map((hex) => { + const key = PublicKey.deserialize(fromHexString(hex), CoordType.jacobian); + key.keyValidate(); + return key; + }), + sig ); } catch (e) { return false; @@ -80,12 +99,17 @@ function fast_aggregate_verify(input: {pubkeys: string[]; message: string; signa function batch_verify(input: {pubkeys: string[]; messages: string[]; signatures: string[]}): boolean | null { const {pubkeys, messages, signatures} = input; try { - return bls.Signature.verifyMultipleSignatures( - pubkeys.map((pubkey, i) => ({ - publicKey: bls.PublicKey.fromBytes(fromHexString(pubkey), CoordType.jacobian, true), - message: fromHexString(messages[i]), - signature: bls.Signature.fromBytes(fromHexString(signatures[i]), undefined, true), - })) + return verifyMultipleAggregateSignatures( + pubkeys.map((pubkey, i) => { + const publicKey = PublicKey.deserialize(fromHexString(pubkey), CoordType.jacobian); + publicKey.keyValidate(); + const signature = signatureFromBytes(fromHexString(signatures[i])); + return { + publicKey, + message: fromHexString(messages[i]), + signature, + }; + }) ); } catch (e) { return false; @@ -103,7 +127,8 @@ function batch_verify(input: {pubkeys: string[]; messages: string[]; signatures: */ function sign(input: {privkey: string; message: string}): string | null { const {privkey, message} = input; - const signature = bls.sign(fromHexString(privkey), fromHexString(message)); + const sk = SecretKey.deserialize(fromHexString(privkey)); + const signature = sk.sign(fromHexString(message)).serialize(); return toHexString(signature); } @@ -119,7 +144,11 @@ function sign(input: {privkey: string; message: string}): string | null { */ function verify(input: {pubkey: string; message: string; signature: string}): boolean { const {pubkey, message, signature} = input; - return bls.verify(fromHexString(pubkey), fromHexString(message), fromHexString(signature)); + try { + return _verify(fromHexString(message), fromHexString(pubkey), fromHexString(signature)); + } catch { + return false; + } } /** @@ -131,7 +160,8 @@ function verify(input: {pubkey: string; message: string; signature: string}): bo */ function deserialization_G1(input: {pubkey: string}): boolean { try { - bls.PublicKey.fromBytes(fromHexString(input.pubkey), CoordType.jacobian, true); + const pk = PublicKey.deserialize(fromHexString(input.pubkey), CoordType.jacobian); + pk.keyValidate(); return true; } catch (e) { return false; @@ -147,7 +177,7 @@ function deserialization_G1(input: {pubkey: string}): boolean { */ function deserialization_G2(input: {signature: string}): boolean { try { - bls.Signature.fromBytes(fromHexString(input.signature), undefined, true); + signatureFromBytes(fromHexString(input.signature)); return true; } catch (e) { return false; diff --git a/packages/beacon-node/test/spec/general/bls.ts b/packages/beacon-node/test/spec/general/bls.ts index 88c1d79bcb79..b9d41cd2786e 100644 --- a/packages/beacon-node/test/spec/general/bls.ts +++ b/packages/beacon-node/test/spec/general/bls.ts @@ -1,8 +1,17 @@ -import bls from "@chainsafe/bls"; -import {CoordType} from "@chainsafe/bls/types"; +import { + CoordType, + PublicKey, + SecretKey, + Signature, + aggregatePublicKeys, + aggregateSignatures, + aggregateVerify, + fastAggregateVerify, + verify as _verify, +} from "@chainsafe/blst"; import {fromHexString} from "@chainsafe/ssz"; import {InputType} from "@lodestar/spec-test-util"; -import {toHexString} from "@lodestar/utils"; +import {signatureFromBytes, toHexString} from "@lodestar/utils"; import {TestRunnerFn} from "../utils/types.js"; /* eslint-disable @typescript-eslint/naming-convention */ @@ -64,8 +73,8 @@ type BlsTestCase = { * ``` */ function aggregate(input: string[]): string { - const pks = input.map((pkHex) => bls.Signature.fromHex(pkHex)); - const agg = bls.Signature.aggregate(pks); + const pks = input.map((pkHex) => Signature.deserialize(fromHexString(pkHex))); + const agg = aggregateSignatures(pks); return agg.toHex(); } @@ -80,7 +89,11 @@ function aggregate(input: string[]): string { */ function aggregate_verify(input: {pubkeys: string[]; messages: string[]; signature: string}): boolean { const {pubkeys, messages, signature} = input; - return bls.verifyMultiple(pubkeys.map(fromHexString), messages.map(fromHexString), fromHexString(signature)); + try { + return aggregateVerify(messages.map(fromHexString), pubkeys.map(fromHexString), fromHexString(signature)); + } catch { + return false; + } } /** @@ -95,7 +108,7 @@ function eth_aggregate_pubkeys(input: string[]): string | null { if (pk === G1_POINT_AT_INFINITY) return null; } - const agg = bls.aggregatePublicKeys(input.map((hex) => fromHexString(hex))); + const agg = aggregatePublicKeys(input.map((hex) => fromHexString(hex))).serialize(); return toHexString(agg); } @@ -120,9 +133,9 @@ function eth_fast_aggregate_verify(input: {pubkeys: string[]; message: string; s if (pk === G1_POINT_AT_INFINITY) return false; } - return bls.verifyAggregate( - pubkeys.map((hex) => fromHexString(hex)), + return fastAggregateVerify( fromHexString(message), + pubkeys.map((hex) => fromHexString(hex)), fromHexString(signature) ); } @@ -139,9 +152,15 @@ function eth_fast_aggregate_verify(input: {pubkeys: string[]; message: string; s function fast_aggregate_verify(input: {pubkeys: string[]; message: string; signature: string}): boolean | null { const {pubkeys, message, signature} = input; try { - return bls.Signature.fromBytes(fromHexString(signature), undefined, true).verifyAggregate( - pubkeys.map((hex) => bls.PublicKey.fromBytes(fromHexString(hex), CoordType.jacobian, true)), - fromHexString(message) + const sig = signatureFromBytes(fromHexString(signature)); + return fastAggregateVerify( + fromHexString(message), + pubkeys.map((hex) => { + const pk = PublicKey.deserialize(fromHexString(hex), CoordType.jacobian); + pk.keyValidate(); + return pk; + }), + sig ); } catch (e) { return false; @@ -156,7 +175,7 @@ function fast_aggregate_verify(input: {pubkeys: string[]; message: string; signa */ function sign(input: {privkey: string; message: string}): string | null { const {privkey, message} = input; - const signature = bls.sign(fromHexString(privkey), fromHexString(message)); + const signature = SecretKey.deserialize(fromHexString(privkey)).sign(fromHexString(message)).serialize(); return toHexString(signature); } @@ -169,5 +188,9 @@ function sign(input: {privkey: string; message: string}): string | null { */ function verify(input: {pubkey: string; message: string; signature: string}): boolean { const {pubkey, message, signature} = input; - return bls.verify(fromHexString(pubkey), fromHexString(message), fromHexString(signature)); + try { + return _verify(fromHexString(message), fromHexString(pubkey), fromHexString(signature)); + } catch { + return false; + } } diff --git a/packages/beacon-node/test/unit/chain/bls/bls.test.ts b/packages/beacon-node/test/unit/chain/bls/bls.test.ts index 9bff307d3d86..7a30663e6381 100644 --- a/packages/beacon-node/test/unit/chain/bls/bls.test.ts +++ b/packages/beacon-node/test/unit/chain/bls/bls.test.ts @@ -1,7 +1,7 @@ -import bls from "@chainsafe/bls"; -import {CoordType, PublicKey} from "@chainsafe/bls/types"; +import {PublicKey, SecretKey} from "@chainsafe/blst"; import {describe, it, expect, beforeEach} from "vitest"; import {ISignatureSet, SignatureSetType} from "@lodestar/state-transition"; +import {signatureFromBytes} from "@lodestar/utils"; import {BlsSingleThreadVerifier} from "../../../../src/chain/bls/singleThread.js"; import {BlsMultiThreadWorkerPool} from "../../../../src/chain/bls/index.js"; import {testLogger} from "../../../utils/logger.js"; @@ -9,7 +9,7 @@ import {testLogger} from "../../../utils/logger.js"; describe("BlsVerifier ", function () { // take time for creating thread pool const numKeys = 3; - const secretKeys = Array.from({length: numKeys}, (_, i) => bls.SecretKey.fromKeygen(Buffer.alloc(32, i))); + const secretKeys = Array.from({length: numKeys}, (_, i) => SecretKey.fromKeygen(Buffer.alloc(32, i))); const verifiers = [ new BlsSingleThreadVerifier({metrics: null}), new BlsMultiThreadWorkerPool({}, {metrics: null, logger: testLogger()}), @@ -27,7 +27,7 @@ describe("BlsVerifier ", function () { type: SignatureSetType.single, pubkey: secretKey.toPublicKey(), signingRoot, - signature: secretKey.sign(signingRoot).toBytes(), + signature: secretKey.sign(signingRoot).serialize(), }; }); }); @@ -45,7 +45,9 @@ describe("BlsVerifier ", function () { it("should return false if at least one signature is malformed", async () => { // signature is malformed const malformedSignature = Buffer.alloc(96, 10); - expect(() => bls.Signature.fromBytes(malformedSignature, CoordType.affine, true)).toThrow(); + expect(() => { + signatureFromBytes(malformedSignature); + }).toThrow(); sets[1].signature = malformedSignature; expect(await verifier.verifySignatureSets(sets)).toBe(false); }); @@ -60,7 +62,7 @@ describe("BlsVerifier ", function () { sets = secretKeys.map((secretKey) => { return { publicKey: secretKey.toPublicKey(), - signature: secretKey.sign(signingRoot).toBytes(), + signature: secretKey.sign(signingRoot).serialize(), }; }); }); @@ -71,14 +73,16 @@ describe("BlsVerifier ", function () { it("should return false for invalid signature", async () => { // signature is valid but not respective to the signing root - sets[1].signature = secretKeys[1].sign(Buffer.alloc(32)).toBytes(); + sets[1].signature = secretKeys[1].sign(Buffer.alloc(32)).serialize(); expect(await verifier.verifySignatureSetsSameMessage(sets, signingRoot)).toEqual([true, false, true]); }); it("should return false for malformed signature", async () => { // signature is malformed const malformedSignature = Buffer.alloc(96, 10); - expect(() => bls.Signature.fromBytes(malformedSignature, CoordType.affine, true)).toThrow(); + expect(() => { + signatureFromBytes(malformedSignature); + }).toThrow(); sets[1].signature = malformedSignature; expect(await verifier.verifySignatureSetsSameMessage(sets, signingRoot)).toEqual([true, false, true]); }); diff --git a/packages/beacon-node/test/unit/chain/genesis/genesis.test.ts b/packages/beacon-node/test/unit/chain/genesis/genesis.test.ts index 986ca074242f..2e00821aa928 100644 --- a/packages/beacon-node/test/unit/chain/genesis/genesis.test.ts +++ b/packages/beacon-node/test/unit/chain/genesis/genesis.test.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import type {SecretKey, PublicKey} from "@chainsafe/bls/types"; +import {SecretKey, PublicKey} from "@chainsafe/blst"; import {toHexString} from "@chainsafe/ssz"; import {describe, it, expect} from "vitest"; import {DOMAIN_DEPOSIT, MAX_EFFECTIVE_BALANCE} from "@lodestar/params"; @@ -111,11 +111,11 @@ describe("genesis builder", function () { function generateDeposit(index: ValidatorIndex, secretKey: SecretKey, publicKey: PublicKey): phase0.DepositData { const domain = computeDomain(DOMAIN_DEPOSIT, config.GENESIS_FORK_VERSION, ZERO_HASH); const depositMessage = { - pubkey: publicKey.toBytes(), + pubkey: publicKey.serialize(), withdrawalCredentials: Buffer.alloc(32, index), amount: MAX_EFFECTIVE_BALANCE, }; const signingRoot = computeSigningRoot(ssz.phase0.DepositMessage, depositMessage, domain); const signature = secretKey.sign(signingRoot); - return {...depositMessage, signature: signature.toBytes()}; + return {...depositMessage, signature: signature.serialize()}; } diff --git a/packages/beacon-node/test/unit/chain/opPools/aggregatedAttestationPool.test.ts b/packages/beacon-node/test/unit/chain/opPools/aggregatedAttestationPool.test.ts index 3c248ad4d194..7c4c74bfc330 100644 --- a/packages/beacon-node/test/unit/chain/opPools/aggregatedAttestationPool.test.ts +++ b/packages/beacon-node/test/unit/chain/opPools/aggregatedAttestationPool.test.ts @@ -1,11 +1,11 @@ -import type {SecretKey} from "@chainsafe/bls/types"; -import bls from "@chainsafe/bls"; +import {SecretKey, fastAggregateVerify} from "@chainsafe/blst"; import {BitArray, fromHexString, toHexString} from "@chainsafe/ssz"; import {describe, it, expect, beforeEach, beforeAll, afterEach, vi} from "vitest"; import {CachedBeaconStateAllForks, newFilledArray} from "@lodestar/state-transition"; import {FAR_FUTURE_EPOCH, ForkName, MAX_EFFECTIVE_BALANCE, SLOTS_PER_EPOCH} from "@lodestar/params"; import {ssz, phase0} from "@lodestar/types"; import {CachedBeaconStateAltair} from "@lodestar/state-transition/src/types.js"; +import {signatureFromBytes} from "@lodestar/utils"; import {MockedForkChoice, getMockedForkChoice} from "../../../mocks/mockedBeaconChain.js"; import { AggregatedAttestationPool, @@ -303,10 +303,10 @@ describe("MatchingDataAttestationGroup aggregateInto", function () { let sk2: SecretKey; beforeAll(async () => { - sk1 = bls.SecretKey.fromBytes(Buffer.alloc(32, 1)); - sk2 = bls.SecretKey.fromBytes(Buffer.alloc(32, 2)); - attestation1.signature = sk1.sign(attestationDataRoot).toBytes(); - attestation2.signature = sk2.sign(attestationDataRoot).toBytes(); + sk1 = SecretKey.deserialize(Buffer.alloc(32, 1)); + sk2 = SecretKey.deserialize(Buffer.alloc(32, 2)); + attestation1.signature = sk1.sign(attestationDataRoot).serialize(); + attestation2.signature = sk2.sign(attestationDataRoot).serialize(); }); it("should aggregate 2 attestations", () => { @@ -315,7 +315,9 @@ describe("MatchingDataAttestationGroup aggregateInto", function () { aggregateInto(attWithIndex1, attWithIndex2); expect(renderBitArray(attWithIndex1.attestation.aggregationBits)).toEqual(renderBitArray(mergedBitArray)); - const aggregatedSignature = bls.Signature.fromBytes(attWithIndex1.attestation.signature, undefined, true); - expect(aggregatedSignature.verifyAggregate([sk1.toPublicKey(), sk2.toPublicKey()], attestationDataRoot)).toBe(true); + const aggregatedSignature = signatureFromBytes(attWithIndex1.attestation.signature); + expect(fastAggregateVerify(attestationDataRoot, [sk1.toPublicKey(), sk2.toPublicKey()], aggregatedSignature)).toBe( + true + ); }); }); diff --git a/packages/beacon-node/test/unit/chain/opPools/syncCommittee.test.ts b/packages/beacon-node/test/unit/chain/opPools/syncCommittee.test.ts index 3cb5d496bf9b..7dcea7bba233 100644 --- a/packages/beacon-node/test/unit/chain/opPools/syncCommittee.test.ts +++ b/packages/beacon-node/test/unit/chain/opPools/syncCommittee.test.ts @@ -1,6 +1,6 @@ -import bls from "@chainsafe/bls"; import {toHexString} from "@chainsafe/ssz"; import {describe, it, expect, beforeEach, beforeAll, afterEach, vi, MockedObject} from "vitest"; +import {SecretKey} from "@chainsafe/blst"; import {altair} from "@lodestar/types"; import {SyncCommitteeMessagePool} from "../../../../src/chain/opPools/index.js"; import {Clock} from "../../../../src/util/clock.js"; @@ -18,12 +18,12 @@ describe("chain / opPools / SyncCommitteeMessagePool", function () { const cutOffTime = 1; beforeAll(async () => { - const sk = bls.SecretKey.fromBytes(Buffer.alloc(32, 1)); + const sk = SecretKey.deserialize(Buffer.alloc(32, 1)); syncCommittee = { slot, beaconBlockRoot, validatorIndex: 2000, - signature: sk.sign(beaconBlockRoot).toBytes(), + signature: sk.sign(beaconBlockRoot).serialize(), }; }); @@ -42,13 +42,13 @@ describe("chain / opPools / SyncCommitteeMessagePool", function () { clockStub.secFromSlot.mockReturnValue(0); let contribution = cache.getContribution(subcommitteeIndex, syncCommittee.slot, syncCommittee.beaconBlockRoot); expect(contribution).not.toBeNull(); - const newSecretKey = bls.SecretKey.fromBytes(Buffer.alloc(32, 2)); + const newSecretKey = SecretKey.deserialize(Buffer.alloc(32, 2)); const newSyncCommittee: altair.SyncCommitteeMessage = { slot: syncCommittee.slot, beaconBlockRoot, // different validatorIndex validatorIndex: syncCommittee.validatorIndex + 1, - signature: newSecretKey.sign(beaconBlockRoot).toBytes(), + signature: newSecretKey.sign(beaconBlockRoot).serialize(), }; const newIndicesInSubSyncCommittee = [1]; cache.add(subcommitteeIndex, newSyncCommittee, newIndicesInSubSyncCommittee[0]); diff --git a/packages/beacon-node/test/unit/chain/opPools/syncCommitteeContribution.test.ts b/packages/beacon-node/test/unit/chain/opPools/syncCommitteeContribution.test.ts index dd303673f61e..0e2df6833cb2 100644 --- a/packages/beacon-node/test/unit/chain/opPools/syncCommitteeContribution.test.ts +++ b/packages/beacon-node/test/unit/chain/opPools/syncCommitteeContribution.test.ts @@ -1,5 +1,4 @@ -import type {SecretKey} from "@chainsafe/bls/types"; -import bls from "@chainsafe/bls"; +import {SecretKey, fastAggregateVerify} from "@chainsafe/blst"; import {BitArray} from "@chainsafe/ssz"; import {describe, it, expect, beforeEach, beforeAll} from "vitest"; import {newFilledArray} from "@lodestar/state-transition"; @@ -83,7 +82,7 @@ describe("aggregate", function () { let bestContributionBySubnet: Map; beforeAll(async () => { for (let i = 0; i < SYNC_COMMITTEE_SUBNET_COUNT; i++) { - sks.push(bls.SecretKey.fromBytes(Buffer.alloc(32, i + 1))); + sks.push(SecretKey.deserialize(Buffer.alloc(32, i + 1))); } bestContributionBySubnet = new Map(); }); @@ -98,7 +97,7 @@ describe("aggregate", function () { // first participation of each subnet is true syncSubcommitteeBits: BitArray.fromBoolArray([true, false, false, false, false, false, false, false]), numParticipants: 1, - syncSubcommitteeSignature: sks[subnet].sign(blockRoot).toBytes(), + syncSubcommitteeSignature: sks[subnet].sign(blockRoot).serialize(), }); testSks.push(sks[subnet]); } @@ -112,9 +111,9 @@ describe("aggregate", function () { renderBitArray(BitArray.fromBoolArray(expectSyncCommittees)) ); expect( - bls.verifyAggregate( - testSks.map((sk) => sk.toPublicKey().toBytes()), + fastAggregateVerify( blockRoot, + testSks.map((sk) => sk.toPublicKey().serialize()), syncAggregate.syncCommitteeSignature ) ).toBe(true); diff --git a/packages/beacon-node/test/unit/chain/validation/attestation/validateGossipAttestationsSameAttData.test.ts b/packages/beacon-node/test/unit/chain/validation/attestation/validateGossipAttestationsSameAttData.test.ts index cc6dc26f1a36..acb8dec0c5a3 100644 --- a/packages/beacon-node/test/unit/chain/validation/attestation/validateGossipAttestationsSameAttData.test.ts +++ b/packages/beacon-node/test/unit/chain/validation/attestation/validateGossipAttestationsSameAttData.test.ts @@ -1,5 +1,4 @@ -import bls from "@chainsafe/bls"; -import type {PublicKey, SecretKey} from "@chainsafe/bls/types"; +import {PublicKey, SecretKey} from "@chainsafe/blst"; import {afterEach, beforeEach, describe, expect, it, vi} from "vitest"; import {ForkName} from "@lodestar/params"; import {SignatureSetType} from "@lodestar/state-transition"; @@ -49,7 +48,7 @@ describe("validateGossipAttestationsSameAttData", () => { const bytes = new Uint8Array(32); const dataView = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); dataView.setUint32(0, i + 1, true); - const secretKey = bls.SecretKey.fromBytes(bytes); + const secretKey = SecretKey.deserialize(bytes); const publicKey = secretKey.toPublicKey(); keypair = {secretKey, publicKey}; keypairs.set(i, keypair); @@ -83,12 +82,12 @@ describe("validateGossipAttestationsSameAttData", () => { type: SignatureSetType.single, pubkey: getKeypair(i).publicKey, signingRoot, - signature: getKeypair(i).secretKey.sign(signingRoot).toBytes(), + signature: getKeypair(i).secretKey.sign(signingRoot).serialize(), }; if (isValid) { if (!phase1Result[i]) { // invalid signature - signatureSet.signature = getKeypair(2023).secretKey.sign(signingRoot).toBytes(); + signatureSet.signature = getKeypair(2023).secretKey.sign(signingRoot).serialize(); } phase0Results.push( Promise.resolve({ diff --git a/packages/beacon-node/test/unit/chain/validation/blsToExecutionChange.test.ts b/packages/beacon-node/test/unit/chain/validation/blsToExecutionChange.test.ts index f83b900beb69..b6089957581b 100644 --- a/packages/beacon-node/test/unit/chain/validation/blsToExecutionChange.test.ts +++ b/packages/beacon-node/test/unit/chain/validation/blsToExecutionChange.test.ts @@ -1,7 +1,6 @@ import {digest} from "@chainsafe/as-sha256"; -import bls from "@chainsafe/bls"; -import {PointFormat} from "@chainsafe/bls/types"; import {describe, it, beforeEach, afterEach, vi} from "vitest"; +import {SecretKey} from "@chainsafe/blst"; import {config as defaultConfig} from "@lodestar/config/default"; import {computeSigningRoot} from "@lodestar/state-transition"; import {capella, ssz} from "@lodestar/types"; @@ -29,12 +28,12 @@ describe("validate bls to execution change", () => { // Validator has to be active for long enough stateEmpty.slot = defaultConfig.SHARD_COMMITTEE_PERIOD * SLOTS_PER_EPOCH; // A withdrawal key which we will keep same on the two vals we generate - const wsk = bls.SecretKey.fromKeygen(); + const wsk = SecretKey.fromKeygen(Buffer.alloc(32, 0)); // Generate and add first val - const sk1 = bls.SecretKey.fromKeygen(); - const pubkey1 = sk1.toPublicKey().toBytes(PointFormat.compressed); - const fromBlsPubkey = wsk.toPublicKey().toBytes(PointFormat.compressed); + const sk1 = SecretKey.fromKeygen(Buffer.alloc(32, 1)); + const pubkey1 = sk1.toPublicKey().serialize(); + const fromBlsPubkey = wsk.toPublicKey().serialize(); const withdrawalCredentials = digest(fromBlsPubkey); withdrawalCredentials[0] = BLS_WITHDRAWAL_PREFIX; const validator = ssz.phase0.Validator.toViewDU({ @@ -50,8 +49,8 @@ describe("validate bls to execution change", () => { stateEmpty.validators[0] = validator; // Gen and add second val - const sk2 = bls.SecretKey.fromKeygen(); - const pubkey2 = sk2.toPublicKey().toBytes(PointFormat.compressed); + const sk2 = SecretKey.fromKeygen(Buffer.alloc(32, 2)); + const pubkey2 = sk2.toPublicKey().serialize(); // Set the next validator to already eth1 credential const withdrawalCredentialsTwo = digest(fromBlsPubkey); withdrawalCredentialsTwo[0] = ETH1_ADDRESS_WITHDRAWAL_PREFIX; @@ -81,7 +80,7 @@ describe("validate bls to execution change", () => { const signatureFork = ForkName.phase0; const domain = config.getDomainAtFork(signatureFork, DOMAIN_BLS_TO_EXECUTION_CHANGE); const signingRoot = computeSigningRoot(ssz.capella.BLSToExecutionChange, blsToExecutionChange, domain); - const signedBlsToExecChange = {message: blsToExecutionChange, signature: wsk.sign(signingRoot).toBytes()}; + const signedBlsToExecChange = {message: blsToExecutionChange, signature: wsk.sign(signingRoot).serialize()}; beforeEach(() => { chainStub = getMockedBeaconChain(); diff --git a/packages/beacon-node/test/unit/chain/validation/voluntaryExit.test.ts b/packages/beacon-node/test/unit/chain/validation/voluntaryExit.test.ts index 3966815c28ce..6f79e19954fb 100644 --- a/packages/beacon-node/test/unit/chain/validation/voluntaryExit.test.ts +++ b/packages/beacon-node/test/unit/chain/validation/voluntaryExit.test.ts @@ -1,6 +1,5 @@ -import bls from "@chainsafe/bls"; -import {PointFormat} from "@chainsafe/bls/types"; import {describe, it, beforeEach, beforeAll, vi, afterEach} from "vitest"; +import {SecretKey} from "@chainsafe/blst"; import {config} from "@lodestar/config/default"; import { CachedBeaconStateAllForks, @@ -25,7 +24,7 @@ describe("validate voluntary exit", () => { let opPool: MockedBeaconChain["opPool"]; beforeAll(() => { - const sk = bls.SecretKey.fromKeygen(); + const sk = SecretKey.fromKeygen(Buffer.alloc(32)); const stateEmpty = ssz.phase0.BeaconState.defaultValue(); @@ -34,7 +33,7 @@ describe("validate voluntary exit", () => { // Add a validator that's active since genesis and ready to exit const validator = ssz.phase0.Validator.toViewDU({ - pubkey: sk.toPublicKey().toBytes(PointFormat.compressed), + pubkey: sk.toPublicKey().serialize(), withdrawalCredentials: Buffer.alloc(32, 0), effectiveBalance: 32e9, slashed: false, @@ -55,7 +54,7 @@ describe("validate voluntary exit", () => { stateEmpty.genesisValidatorsRoot ); const signingRoot = computeSigningRoot(ssz.phase0.VoluntaryExit, voluntaryExit, domain); - signedVoluntaryExit = {message: voluntaryExit, signature: sk.sign(signingRoot).toBytes()}; + signedVoluntaryExit = {message: voluntaryExit, signature: sk.sign(signingRoot).serialize()}; const _state = generateState(stateEmpty, config); state = createCachedBeaconStateTest(_state, createBeaconConfig(config, _state.genesisValidatorsRoot)); diff --git a/packages/beacon-node/test/utils/cache.ts b/packages/beacon-node/test/utils/cache.ts index 767956226776..0c5ecbfc41ea 100644 --- a/packages/beacon-node/test/utils/cache.ts +++ b/packages/beacon-node/test/utils/cache.ts @@ -1,4 +1,4 @@ -import type {SecretKey} from "@chainsafe/bls/types"; +import {SecretKey} from "@chainsafe/blst"; import {toHexString} from "@chainsafe/ssz"; export function memoOnce(fn: () => R): () => R { @@ -32,7 +32,7 @@ export function signCached(sk: SecretKey, message: Uint8Array): Uint8Array { return prevSig; } - const sig = sk.sign(message).toBytes(); + const sig = sk.sign(message).serialize(); cache.set(messageHex, sig); return sig; diff --git a/packages/beacon-node/test/utils/node/validator.ts b/packages/beacon-node/test/utils/node/validator.ts index ce81fc6d18ca..a6d1c7dc30f5 100644 --- a/packages/beacon-node/test/utils/node/validator.ts +++ b/packages/beacon-node/test/utils/node/validator.ts @@ -1,5 +1,5 @@ import tmp from "tmp"; -import type {SecretKey} from "@chainsafe/bls/types"; +import {SecretKey} from "@chainsafe/blst"; import {LevelDbController} from "@lodestar/db"; import {interopSecretKey} from "@lodestar/state-transition"; import {SlashingProtection, Validator, Signer, SignerType, ValidatorProposerConfig} from "@lodestar/validator"; diff --git a/packages/beacon-node/test/utils/state.ts b/packages/beacon-node/test/utils/state.ts index c9dc79d34ed0..d9224e13b394 100644 --- a/packages/beacon-node/test/utils/state.ts +++ b/packages/beacon-node/test/utils/state.ts @@ -1,4 +1,4 @@ -import bls from "@chainsafe/bls"; +import {SecretKey} from "@chainsafe/blst"; import {config as minimalConfig} from "@lodestar/config/default"; import { BeaconStateAllForks, @@ -55,10 +55,10 @@ export function generateState( opts.validators ?? (withPubkey ? Array.from({length: numValidators}, (_, i) => { - const sk = bls.SecretKey.fromBytes(Buffer.alloc(32, i + 1)); + const sk = SecretKey.deserialize(Buffer.alloc(32, i + 1)); return generateValidator({ ...validatorOpts, - pubkey: sk.toPublicKey().toBytes(), + pubkey: sk.toPublicKey().serialize(), }); }) : generateValidators(numValidators, validatorOpts)); diff --git a/packages/cli/package.json b/packages/cli/package.json index d201d3d4e4b2..e752e8f3322a 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -51,8 +51,7 @@ "blockchain" ], "dependencies": { - "@chainsafe/bls": "^8.1.0", - "@chainsafe/blst": "^1.0.0", + "@chainsafe/blst": "^1.0.1", "@chainsafe/bls-keygen": "^0.4.0", "@chainsafe/bls-keystore": "^3.0.1", "@chainsafe/discv5": "^9.0.0", diff --git a/packages/cli/src/cmds/dev/files.ts b/packages/cli/src/cmds/dev/files.ts index 9baf0dc845dd..a1a2b35f00b4 100644 --- a/packages/cli/src/cmds/dev/files.ts +++ b/packages/cli/src/cmds/dev/files.ts @@ -40,7 +40,7 @@ export async function writeTestnetFiles( const sk = interopSecretKey(i); - const keystore = await Keystore.create(password, sk.toBytes(), sk.toPublicKey().toBytes(), ""); + const keystore = await Keystore.create(password, sk.serialize(), sk.toPublicKey().serialize(), ""); persistedKeystoresBackend.writeKeystore({ keystoreStr: keystore.stringify(), diff --git a/packages/cli/src/cmds/validator/blsToExecutionChange.ts b/packages/cli/src/cmds/validator/blsToExecutionChange.ts index 7452840e1c71..621817f14227 100644 --- a/packages/cli/src/cmds/validator/blsToExecutionChange.ts +++ b/packages/cli/src/cmds/validator/blsToExecutionChange.ts @@ -1,6 +1,5 @@ import {fromHexString} from "@chainsafe/ssz"; -import bls from "@chainsafe/bls"; -import {PointFormat} from "@chainsafe/bls/types"; +import {SecretKey} from "@chainsafe/blst"; import {computeSigningRoot} from "@lodestar/state-transition"; import {DOMAIN_BLS_TO_EXECUTION_CHANGE, ForkName} from "@lodestar/params"; import {createBeaconConfig} from "@lodestar/config"; @@ -72,8 +71,8 @@ like to choose for BLS To Execution Change.", throw new Error(`Validator pubkey ${publicKey} not found in state`); } - const blsPrivkey = bls.SecretKey.fromBytes(fromHexString(args.fromBlsPrivkey)); - const fromBlsPubkey = blsPrivkey.toPublicKey().toBytes(PointFormat.compressed); + const blsPrivkey = SecretKey.deserialize(fromHexString(args.fromBlsPrivkey)); + const fromBlsPubkey = blsPrivkey.toPublicKey().serialize(); const blsToExecutionChange: capella.BLSToExecutionChange = { validatorIndex: stateValidator.index, @@ -86,7 +85,7 @@ like to choose for BLS To Execution Change.", const signingRoot = computeSigningRoot(ssz.capella.BLSToExecutionChange, blsToExecutionChange, domain); const signedBLSToExecutionChange = { message: blsToExecutionChange, - signature: blsPrivkey.sign(signingRoot).toBytes(), + signature: blsPrivkey.sign(signingRoot).serialize(), }; ApiError.assert( diff --git a/packages/cli/src/cmds/validator/keymanager/decryptKeystoreDefinitions.ts b/packages/cli/src/cmds/validator/keymanager/decryptKeystoreDefinitions.ts index 2901fd6cdfb5..b6efe219deaa 100644 --- a/packages/cli/src/cmds/validator/keymanager/decryptKeystoreDefinitions.ts +++ b/packages/cli/src/cmds/validator/keymanager/decryptKeystoreDefinitions.ts @@ -1,5 +1,5 @@ import path from "node:path"; -import bls from "@chainsafe/bls"; +import {SecretKey} from "@chainsafe/blst"; import {SignerLocal, SignerType} from "@lodestar/validator"; import {LogLevel, Logger} from "@lodestar/utils"; import {lockFilepath, unlockFilepath} from "../../../util/lockfile.js"; @@ -67,7 +67,7 @@ export async function decryptKeystoreDefinitions( (secretKeyBytes: Uint8Array) => { const signer: SignerLocal = { type: SignerType.Local, - secretKey: bls.SecretKey.fromBytes(secretKeyBytes), + secretKey: SecretKey.deserialize(secretKeyBytes), }; signers[index] = signer; diff --git a/packages/cli/src/cmds/validator/keymanager/impl.ts b/packages/cli/src/cmds/validator/keymanager/impl.ts index 3ff7e1af58a2..49fc19f5ac68 100644 --- a/packages/cli/src/cmds/validator/keymanager/impl.ts +++ b/packages/cli/src/cmds/validator/keymanager/impl.ts @@ -1,4 +1,4 @@ -import bls from "@chainsafe/bls"; +import {SecretKey} from "@chainsafe/blst"; import {Keystore} from "@chainsafe/bls-keystore"; import {fromHexString} from "@chainsafe/ssz"; import { @@ -172,7 +172,7 @@ export class KeymanagerApi implements Api { decryptKeystores.queue( {keystoreStr, password}, async (secretKeyBytes: Uint8Array) => { - const secretKey = bls.SecretKey.fromBytes(secretKeyBytes); + const secretKey = SecretKey.deserialize(secretKeyBytes); // Persist the key to disk for restarts, before adding to in-memory store // If the keystore exist and has a lock it will throw diff --git a/packages/cli/src/cmds/validator/keymanager/keystoreCache.ts b/packages/cli/src/cmds/validator/keymanager/keystoreCache.ts index 1f1c6cd6e79f..58dc02265685 100644 --- a/packages/cli/src/cmds/validator/keymanager/keystoreCache.ts +++ b/packages/cli/src/cmds/validator/keymanager/keystoreCache.ts @@ -1,8 +1,7 @@ import fs from "node:fs"; import path from "node:path"; -import bls from "@chainsafe/bls"; +import {SecretKey} from "@chainsafe/blst"; import {Keystore} from "@chainsafe/bls-keystore"; -import {PointFormat} from "@chainsafe/bls/types"; import {SignerLocal, SignerType} from "@lodestar/validator"; import {fromHex, toHex} from "@lodestar/utils"; import {writeFile600Perm} from "../../../util/file.js"; @@ -40,8 +39,8 @@ export async function loadKeystoreCache( const result: SignerLocal[] = []; for (const [index, k] of keystores.entries()) { const secretKeyBytes = Uint8Array.prototype.slice.call(secretKeyConcatenatedBytes, index * 32, (index + 1) * 32); - const secretKey = bls.SecretKey.fromBytes(secretKeyBytes); - const publicKey = secretKey.toPublicKey().toBytes(PointFormat.compressed); + const secretKey = SecretKey.deserialize(secretKeyBytes); + const publicKey = secretKey.toPublicKey().serialize(); if (toHex(publicKey) !== toHex(fromHex(k.pubkey))) { throw new Error( @@ -72,8 +71,8 @@ export async function writeKeystoreCache( `Number of signers and passwords must be equal. signers=${signers.length}, passwords=${passwords.length}` ); } - const secretKeys = signers.map((s) => s.secretKey.toBytes()); - const publicKeys = signers.map((s) => s.secretKey.toPublicKey().toBytes()); + const secretKeys = signers.map((s) => s.secretKey.serialize()); + const publicKeys = signers.map((s) => s.secretKey.toPublicKey().serialize()); const password = passwords.join(""); const secretKeyConcatenatedBytes = Buffer.concat(secretKeys); const publicConcatenatedBytes = Buffer.concat(publicKeys); diff --git a/packages/cli/src/cmds/validator/signers/index.ts b/packages/cli/src/cmds/validator/signers/index.ts index be028461c0ee..084657619008 100644 --- a/packages/cli/src/cmds/validator/signers/index.ts +++ b/packages/cli/src/cmds/validator/signers/index.ts @@ -1,5 +1,5 @@ import path from "node:path"; -import bls from "@chainsafe/bls"; +import {SecretKey} from "@chainsafe/blst"; import {deriveEth2ValidatorKeys, deriveKeyFromMnemonic} from "@chainsafe/bls-keygen"; import {toHexString} from "@chainsafe/ssz"; import {interopSecretKey} from "@lodestar/state-transition"; @@ -72,7 +72,7 @@ export async function getSignersFromArgs( const indexes = parseRange(args.mnemonicIndexes); return indexes.map((index) => ({ type: SignerType.Local, - secretKey: bls.SecretKey.fromBytes(deriveEth2ValidatorKeys(masterSK, index).signing), + secretKey: SecretKey.deserialize(deriveEth2ValidatorKeys(masterSK, index).signing), })); } @@ -148,7 +148,7 @@ export async function getSignersFromArgs( export function getSignerPubkeyHex(signer: Signer): string { switch (signer.type) { case SignerType.Local: - return toHexString(signer.secretKey.toPublicKey().toBytes()); + return toHexString(signer.secretKey.toPublicKey().serialize()); case SignerType.Remote: return signer.pubkey; diff --git a/packages/cli/src/cmds/validator/voluntaryExit.ts b/packages/cli/src/cmds/validator/voluntaryExit.ts index 89a908516f03..4794d9c5d32b 100644 --- a/packages/cli/src/cmds/validator/voluntaryExit.ts +++ b/packages/cli/src/cmds/validator/voluntaryExit.ts @@ -1,5 +1,5 @@ import inquirer from "inquirer"; -import bls from "@chainsafe/bls"; +import {Signature} from "@chainsafe/blst"; import { computeEpochAtSlot, computeSigningRoot, @@ -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, toHex} from "@lodestar/utils"; +import {CliCommand, fromHex, toHex} from "@lodestar/utils"; import {externalSignerPostSignature, SignableMessageType, Signer, SignerType} from "@lodestar/validator"; import {Api, ApiError, getClient} from "@lodestar/api"; import {ensure0xPrefix, YargsError, wrapError} from "../../util/index.js"; @@ -163,7 +163,7 @@ async function processVoluntaryExit( data: voluntaryExit, type: SignableMessageType.VOLUNTARY_EXIT, }); - signature = bls.Signature.fromHex(signatureHex); + signature = Signature.deserialize(fromHex(signatureHex)); break; } default: @@ -173,7 +173,7 @@ async function processVoluntaryExit( ApiError.assert( await client.beacon.submitPoolVoluntaryExit({ message: voluntaryExit, - signature: signature.toBytes(), + signature: signature.serialize(), }) ); } diff --git a/packages/cli/src/util/format.ts b/packages/cli/src/util/format.ts index b26da62527d1..224590a86671 100644 --- a/packages/cli/src/util/format.ts +++ b/packages/cli/src/util/format.ts @@ -1,5 +1,4 @@ -import bls from "@chainsafe/bls"; -import {CoordType} from "@chainsafe/bls/types"; +import {CoordType, PublicKey} from "@chainsafe/blst"; import {fromHexString} from "@chainsafe/ssz"; /** @@ -52,7 +51,7 @@ export function parseRange(range: string): number[] { export function assertValidPubkeysHex(pubkeysHex: string[]): void { for (const pubkeyHex of pubkeysHex) { const pubkeyBytes = fromHexString(pubkeyHex); - bls.PublicKey.fromBytes(pubkeyBytes, CoordType.jacobian, true); + PublicKey.deserialize(pubkeyBytes, CoordType.jacobian).keyValidate(); } } diff --git a/packages/cli/test/unit/cmds/validator/keymanager/keystoreCache.test.ts b/packages/cli/test/unit/cmds/validator/keymanager/keystoreCache.test.ts index 40964a0a7b7d..6164542a8d7f 100644 --- a/packages/cli/test/unit/cmds/validator/keymanager/keystoreCache.test.ts +++ b/packages/cli/test/unit/cmds/validator/keymanager/keystoreCache.test.ts @@ -3,7 +3,6 @@ import {randomBytes} from "node:crypto"; import {describe, it, expect, beforeEach, vi} from "vitest"; import tmp from "tmp"; import {Keystore} from "@chainsafe/bls-keystore"; -import bls from "@chainsafe/bls"; import {interopSecretKey} from "@lodestar/state-transition"; import {SignerLocal, SignerType} from "@lodestar/validator"; import {loadKeystoreCache, writeKeystoreCache} from "../../../../../src/cmds/validator/keymanager/keystoreCache.js"; @@ -27,13 +26,13 @@ describe("keystoreCache", () => { keystoreCacheFile = tmp.tmpNameSync({postfix: ".cache"}); for (let i = 0; i < numberOfSigners; i++) { - const secretKey = bls.SecretKey.fromBytes(interopSecretKey(i).toBytes()); + const secretKey = interopSecretKey(i); const keystorePath = tmp.tmpNameSync({postfix: ".json"}); const password = secretKey.toHex(); const keystore = await Keystore.create( password, - secretKey.toBytes(), - secretKey.toPublicKey().toBytes(), + secretKey.serialize(), + secretKey.toPublicKey().serialize(), keystorePath, "test-keystore", // To make the test efficient we use a low iteration count @@ -49,7 +48,7 @@ describe("keystoreCache", () => { // Use secretkey hex as password definitions.push({password: secretKey.toHex(), keystorePath}); passwords.push(password); - secretKeys.push(secretKey.toBytes()); + secretKeys.push(secretKey.serialize()); } }); @@ -71,7 +70,7 @@ describe("keystoreCache", () => { await writeKeystoreCache(keystoreCacheFile, signers, passwords); const result = await loadKeystoreCache(keystoreCacheFile, definitions); - expect(result.map((r) => r.secretKey.toBytes())).toEqual(secretKeys); + expect(result.map((r) => r.secretKey.serialize())).toEqual(secretKeys); }); it("should raise error for mismatch public key", async () => { diff --git a/packages/cli/test/utils/cachedKeys.ts b/packages/cli/test/utils/cachedKeys.ts index f2fe6529f58f..b53fab7f2037 100644 --- a/packages/cli/test/utils/cachedKeys.ts +++ b/packages/cli/test/utils/cachedKeys.ts @@ -1,5 +1,5 @@ // // Uncomment code below to re-generate the keys -// import bls from "@chainsafe/bls"; +// import bls from "@chainsafe/blst"; // const sks: string[] = []; // const pks: string[] = []; // for (let i = 0; i < 4; i++) { diff --git a/packages/cli/test/utils/crucible/assertions/nodeAssertion.ts b/packages/cli/test/utils/crucible/assertions/nodeAssertion.ts index 7a449b3f669f..49231a4b2f21 100644 --- a/packages/cli/test/utils/crucible/assertions/nodeAssertion.ts +++ b/packages/cli/test/utils/crucible/assertions/nodeAssertion.ts @@ -1,4 +1,4 @@ -import type {SecretKey} from "@chainsafe/bls/types"; +import {SecretKey} from "@chainsafe/blst"; import {routes} from "@lodestar/api/beacon"; import {ApiError} from "@lodestar/api"; import {AssertionResult, ValidatorClientKeys, Assertion, ValidatorClient} from "../interfaces.js"; diff --git a/packages/cli/test/utils/crucible/externalSignerServer.ts b/packages/cli/test/utils/crucible/externalSignerServer.ts index 25071d1ee545..c3f2edcece50 100644 --- a/packages/cli/test/utils/crucible/externalSignerServer.ts +++ b/packages/cli/test/utils/crucible/externalSignerServer.ts @@ -1,4 +1,4 @@ -import type {SecretKey} from "@chainsafe/bls/types"; +import {SecretKey} from "@chainsafe/blst"; import {fromHexString} from "@chainsafe/ssz"; import {fastify, FastifyInstance} from "fastify"; import {EXTERNAL_SIGNER_BASE_PORT} from "./constants.js"; diff --git a/packages/cli/test/utils/crucible/interfaces.ts b/packages/cli/test/utils/crucible/interfaces.ts index a7934f962d4b..5f682bc09d91 100644 --- a/packages/cli/test/utils/crucible/interfaces.ts +++ b/packages/cli/test/utils/crucible/interfaces.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ import {ChildProcess} from "node:child_process"; -import type {SecretKey} from "@chainsafe/bls/types"; +import {SecretKey} from "@chainsafe/blst"; import {Web3} from "web3"; import {Api} from "@lodestar/api"; import {Api as KeyManagerApi} from "@lodestar/api/keymanager"; diff --git a/packages/cli/test/utils/crucible/utils/keys.ts b/packages/cli/test/utils/crucible/utils/keys.ts index 27983c04cd38..843662239f70 100644 --- a/packages/cli/test/utils/crucible/utils/keys.ts +++ b/packages/cli/test/utils/crucible/utils/keys.ts @@ -22,7 +22,12 @@ export const createKeystores = async ( if (keys.type === "local") { for (const key of keys.secretKeys) { - const keystore = await Keystore.create(SHARED_VALIDATOR_PASSWORD, key.toBytes(), key.toPublicKey().toBytes(), ""); + const keystore = await Keystore.create( + SHARED_VALIDATOR_PASSWORD, + key.serialize(), + key.toPublicKey().serialize(), + "" + ); await writeFile( path.join(keystoresDir, `${key.toPublicKey().toHex()}.json`), diff --git a/packages/flare/package.json b/packages/flare/package.json index 2ec810d8e3c1..84baea056d2d 100644 --- a/packages/flare/package.json +++ b/packages/flare/package.json @@ -58,8 +58,7 @@ "blockchain" ], "dependencies": { - "@chainsafe/bls": "^8.1.0", - "@chainsafe/blst": "^1.0.0", + "@chainsafe/blst": "^1.0.1", "@chainsafe/bls-keygen": "^0.4.0", "@lodestar/api": "^1.18.1", "@lodestar/config": "^1.18.1", diff --git a/packages/flare/src/cmds/selfSlashAttester.ts b/packages/flare/src/cmds/selfSlashAttester.ts index beaa14ed9291..5a8f947d1b2d 100644 --- a/packages/flare/src/cmds/selfSlashAttester.ts +++ b/packages/flare/src/cmds/selfSlashAttester.ts @@ -1,5 +1,4 @@ -import bls from "@chainsafe/bls"; -import type {SecretKey} from "@chainsafe/bls/types"; +import {SecretKey, aggregateSignatures} from "@chainsafe/blst"; import {ApiError, getClient} from "@lodestar/api"; import {phase0, ssz} from "@lodestar/types"; import {config as chainConfig} from "@lodestar/config/default"; @@ -152,5 +151,5 @@ function signAttestationDataBigint( const signingRoot = computeSigningRoot(ssz.phase0.AttestationDataBigint, data, proposerDomain); const sigs = sks.map((sk) => sk.sign(signingRoot)); - return bls.Signature.aggregate(sigs).toBytes(); + return aggregateSignatures(sigs).serialize(); } diff --git a/packages/flare/src/cmds/selfSlashProposer.ts b/packages/flare/src/cmds/selfSlashProposer.ts index ba8a85bc8e71..93df5b67e86c 100644 --- a/packages/flare/src/cmds/selfSlashProposer.ts +++ b/packages/flare/src/cmds/selfSlashProposer.ts @@ -1,4 +1,4 @@ -import type {SecretKey} from "@chainsafe/bls/types"; +import {SecretKey} from "@chainsafe/blst"; import {ApiError, getClient} from "@lodestar/api"; import {phase0, ssz} from "@lodestar/types"; import {config as chainConfig} from "@lodestar/config/default"; @@ -139,5 +139,5 @@ function signHeaderBigint(config: BeaconConfig, sk: SecretKey, header: phase0.Be const slot = Number(header.slot as bigint); const proposerDomain = config.getDomain(slot, DOMAIN_BEACON_PROPOSER); const signingRoot = computeSigningRoot(ssz.phase0.BeaconBlockHeaderBigint, header, proposerDomain); - return sk.sign(signingRoot).toBytes(); + return sk.sign(signingRoot).serialize(); } diff --git a/packages/flare/src/util/deriveSecretKeys.ts b/packages/flare/src/util/deriveSecretKeys.ts index 272cf87c09c4..58c75fa27107 100644 --- a/packages/flare/src/util/deriveSecretKeys.ts +++ b/packages/flare/src/util/deriveSecretKeys.ts @@ -1,5 +1,4 @@ -import bls from "@chainsafe/bls"; -import type {SecretKey} from "@chainsafe/bls/types"; +import {SecretKey} from "@chainsafe/blst"; import {deriveEth2ValidatorKeys, deriveKeyFromMnemonic} from "@chainsafe/bls-keygen"; import {interopSecretKey} from "@lodestar/state-transition"; import {CliCommandOptions} from "@lodestar/utils"; @@ -42,7 +41,7 @@ export function deriveSecretKeys(args: SecretKeysArgs): SecretKey[] { return indexes.map((index) => { const {signing} = deriveEth2ValidatorKeys(masterSK, index); - return bls.SecretKey.fromBytes(signing); + return SecretKey.deserialize(signing); }); } diff --git a/packages/state-transition/package.json b/packages/state-transition/package.json index 0d68d64f3451..3d5053c43516 100644 --- a/packages/state-transition/package.json +++ b/packages/state-transition/package.json @@ -59,8 +59,7 @@ "types": "lib/index.d.ts", "dependencies": { "@chainsafe/as-sha256": "^0.4.1", - "@chainsafe/bls": "^8.1.0", - "@chainsafe/blst": "^1.0.0", + "@chainsafe/blst": "^1.0.1", "@chainsafe/persistent-merkle-tree": "^0.7.1", "@chainsafe/persistent-ts": "^0.19.1", "@chainsafe/ssz": "^0.15.1", diff --git a/packages/state-transition/src/block/processDeposit.ts b/packages/state-transition/src/block/processDeposit.ts index dae37d5d0afe..ce11dbe8d25e 100644 --- a/packages/state-transition/src/block/processDeposit.ts +++ b/packages/state-transition/src/block/processDeposit.ts @@ -1,7 +1,6 @@ -import bls from "@chainsafe/bls"; -import {CoordType} from "@chainsafe/bls/types"; +import {CoordType, PublicKey, verify} from "@chainsafe/blst"; import {phase0, ssz} from "@lodestar/types"; -import {verifyMerkleBranch} from "@lodestar/utils"; +import {signatureFromBytes, verifyMerkleBranch} from "@lodestar/utils"; import { DEPOSIT_CONTRACT_TREE_DEPTH, @@ -56,9 +55,10 @@ export function processDeposit(fork: ForkSeq, state: CachedBeaconStateAllForks, const signingRoot = computeSigningRoot(ssz.phase0.DepositMessage, depositMessage, domain); try { // Pubkeys must be checked for group + inf. This must be done only once when the validator deposit is processed - const publicKey = bls.PublicKey.fromBytes(pubkey, CoordType.affine, true); - const signature = bls.Signature.fromBytes(deposit.data.signature, CoordType.affine, true); - if (!signature.verify(publicKey, signingRoot)) { + const publicKey = PublicKey.deserialize(pubkey, CoordType.affine); + publicKey.keyValidate(); + const signature = signatureFromBytes(deposit.data.signature); + if (!verify(signingRoot, publicKey, signature)) { return; } } catch (e) { diff --git a/packages/state-transition/src/cache/epochCache.ts b/packages/state-transition/src/cache/epochCache.ts index 9565898eb09d..dcd6ac0f0d80 100644 --- a/packages/state-transition/src/cache/epochCache.ts +++ b/packages/state-transition/src/cache/epochCache.ts @@ -1,5 +1,4 @@ -import {CoordType} from "@chainsafe/bls/types"; -import bls from "@chainsafe/bls"; +import {CoordType, PublicKey} from "@chainsafe/blst"; import {BLSSignature, CommitteeIndex, Epoch, Slot, ValidatorIndex, phase0, SyncPeriod} from "@lodestar/types"; import {createBeaconConfig, BeaconConfig, ChainConfig} from "@lodestar/config"; import { @@ -762,7 +761,7 @@ export class EpochCache { addPubkey(index: ValidatorIndex, pubkey: Uint8Array): void { this.pubkey2index.set(pubkey, index); - this.index2pubkey[index] = bls.PublicKey.fromBytes(pubkey, CoordType.jacobian); // Optimize for aggregation + this.index2pubkey[index] = PublicKey.deserialize(pubkey, CoordType.jacobian); // Optimize for aggregation } getShufflingAtSlot(slot: Slot): EpochShuffling { diff --git a/packages/state-transition/src/cache/pubkeyCache.ts b/packages/state-transition/src/cache/pubkeyCache.ts index 64a3f4f23c8f..92718fcc38f7 100644 --- a/packages/state-transition/src/cache/pubkeyCache.ts +++ b/packages/state-transition/src/cache/pubkeyCache.ts @@ -1,5 +1,4 @@ -import {CoordType, PublicKey} from "@chainsafe/bls/types"; -import bls from "@chainsafe/bls"; +import {CoordType, PublicKey} from "@chainsafe/blst"; import {ValidatorIndex} from "@lodestar/types"; import {BeaconStateAllForks} from "./types.js"; @@ -72,6 +71,6 @@ export function syncPubkeys( // Pubkeys must be checked for group + inf. This must be done only once when the validator deposit is processed. // Afterwards any public key is the state consider validated. // > Do not do any validation here - index2pubkey.push(bls.PublicKey.fromBytes(pubkey, CoordType.jacobian)); // Optimize for aggregation + index2pubkey.push(PublicKey.deserialize(pubkey, CoordType.jacobian)); // Optimize for aggregation } } diff --git a/packages/state-transition/src/cache/stateCache.ts b/packages/state-transition/src/cache/stateCache.ts index 8b45152a3646..482f5bd8ff40 100644 --- a/packages/state-transition/src/cache/stateCache.ts +++ b/packages/state-transition/src/cache/stateCache.ts @@ -1,5 +1,4 @@ -import bls from "@chainsafe/bls"; -import {CoordType} from "@chainsafe/bls/types"; +import {CoordType, PublicKey} from "@chainsafe/blst"; import {BeaconConfig} from "@lodestar/config"; import {loadState} from "../util/loadState/loadState.js"; import {EpochCache, EpochCacheImmutableData, EpochCacheOpts} from "./epochCache.js"; @@ -180,7 +179,7 @@ export function loadCachedBeaconState bls.PublicKey.fromBytes(pk, CoordType.jacobian)); + const pubkeysModObj = pubkeysMod.map((pk) => PublicKey.deserialize(pk, CoordType.jacobian)); const pubkeys = Array.from({length: vc}, (_, i) => pubkeysMod[i % keypairsMod]); return {pubkeysMod, pubkeysModObj, pubkeys}; } diff --git a/packages/state-transition/test/perf/util/loadState/loadState.test.ts b/packages/state-transition/test/perf/util/loadState/loadState.test.ts index 5d40c64f6ab4..25d16d0f7aaa 100644 --- a/packages/state-transition/test/perf/util/loadState/loadState.test.ts +++ b/packages/state-transition/test/perf/util/loadState/loadState.test.ts @@ -1,5 +1,4 @@ -import bls from "@chainsafe/bls"; -import {CoordType} from "@chainsafe/bls/types"; +import {CoordType, PublicKey} from "@chainsafe/blst"; import {itBench, setBenchOpts} from "@dapplion/benchmark"; import {loadState} from "../../../../src/util/loadState/loadState.js"; import {createCachedBeaconState} from "../../../../src/cache/stateCache.js"; @@ -78,7 +77,7 @@ describe("loadState", function () { const validator = validators.getReadonly(validatorIndex); const pubkey = validator.pubkey; pubkey2index.set(pubkey, validatorIndex); - index2pubkey[validatorIndex] = bls.PublicKey.fromBytes(pubkey, CoordType.jacobian); + index2pubkey[validatorIndex] = PublicKey.deserialize(pubkey, CoordType.jacobian); } // skip computimg shuffling in performance test because in reality we have a ShufflingCache // eslint-disable-next-line @typescript-eslint/explicit-function-return-type diff --git a/packages/state-transition/test/unit/cachedBeaconState.test.ts b/packages/state-transition/test/unit/cachedBeaconState.test.ts index 2563cf650644..07907fca05f2 100644 --- a/packages/state-transition/test/unit/cachedBeaconState.test.ts +++ b/packages/state-transition/test/unit/cachedBeaconState.test.ts @@ -174,7 +174,7 @@ describe("CachedBeaconState", () => { // confirm loadCachedBeaconState() result for (let i = 0; i < newCachedState.validators.length; i++) { expect(newCachedState.epochCtx.pubkey2index.get(newCachedState.validators.get(i).pubkey)).toBe(i); - expect(Uint8Array.from(newCachedState.epochCtx.index2pubkey[i].toBytes())).toEqual(pubkeys[i]); + expect(Uint8Array.from(newCachedState.epochCtx.index2pubkey[i].serialize())).toEqual(pubkeys[i]); } }); } diff --git a/packages/state-transition/test/unit/signatureSets/signatureSets.test.ts b/packages/state-transition/test/unit/signatureSets/signatureSets.test.ts index 1a5b15e1b041..1383d70e9d48 100644 --- a/packages/state-transition/test/unit/signatureSets/signatureSets.test.ts +++ b/packages/state-transition/test/unit/signatureSets/signatureSets.test.ts @@ -1,7 +1,7 @@ import crypto from "node:crypto"; import {describe, it, expect} from "vitest"; -import bls from "@chainsafe/bls"; import {BitArray} from "@chainsafe/ssz"; +import {SecretKey} from "@chainsafe/blst"; import {config} from "@lodestar/config/default"; import {phase0, capella, ValidatorIndex, BLSSignature, ssz} from "@lodestar/types"; import {FAR_FUTURE_EPOCH, MAX_EFFECTIVE_BALANCE} from "@lodestar/params"; @@ -61,7 +61,9 @@ describe("signatureSets", () => { exit: FAR_FUTURE_EPOCH, }); for (const validator of validators) { - validator.pubkey = bls.SecretKey.fromKeygen().toPublicKey().toBytes(); + validator.pubkey = SecretKey.fromKeygen(crypto.getRandomValues(new Uint8Array(32))) + .toPublicKey() + .serialize(); } const state = generateCachedState(config, {validators}); diff --git a/packages/state-transition/test/utils/interop.ts b/packages/state-transition/test/utils/interop.ts index 5a0dca4e0018..28cb9e5d3fb6 100644 --- a/packages/state-transition/test/utils/interop.ts +++ b/packages/state-transition/test/utils/interop.ts @@ -18,7 +18,7 @@ export function interopPubkeysCached(validatorCount: number): Uint8Array[] { if (cachedKeysHex.length < validatorCount) { for (let i = cachedKeysHex.length; i < validatorCount; i++) { const sk = interopSecretKey(i); - const pk = sk.toPublicKey().toBytes(); + const pk = sk.toPublicKey().serialize(); keys.push(pk); } const keysHex = keys.map((pk) => toHexString(pk)); diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 6fdd80433a88..f5692d013da1 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -57,7 +57,7 @@ "blockchain" ], "dependencies": { - "@chainsafe/bls": "^8.1.0", + "@chainsafe/blst": "^1.0.1", "@chainsafe/bls-keystore": "^3.0.1", "@lodestar/params": "^1.18.1", "@lodestar/utils": "^1.18.1", diff --git a/packages/test-utils/src/keystores.ts b/packages/test-utils/src/keystores.ts index 2321fde30bd4..10ba3f82cf35 100644 --- a/packages/test-utils/src/keystores.ts +++ b/packages/test-utils/src/keystores.ts @@ -1,5 +1,5 @@ -import bls from "@chainsafe/bls"; import {Keystore} from "@chainsafe/bls-keystore"; +import {SecretKey} from "@chainsafe/blst"; import {fromHex} from "@lodestar/utils"; /** @@ -10,7 +10,7 @@ export async function getKeystoresStr(password: string, secretKeys: string[]): P for (const secretKey of secretKeys) { const sk = fromHex(secretKey); - const pk = bls.SecretKey.fromBytes(sk).toPublicKey().toBytes(); + const pk = SecretKey.deserialize(sk).toPublicKey().serialize(); const keystore = await Keystore.create(password, sk, pk, ""); keystoresStr.push(keystore.stringify()); } diff --git a/packages/utils/package.json b/packages/utils/package.json index 99172fdefc19..d47e54d0e05b 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -40,6 +40,7 @@ "types": "lib/index.d.ts", "dependencies": { "@chainsafe/as-sha256": "^0.4.1", + "@chainsafe/blst": "^1.0.1", "any-signal": "3.0.1", "bigint-buffer": "^1.1.5", "case": "^1.6.3", diff --git a/packages/utils/src/bls.ts b/packages/utils/src/bls.ts new file mode 100644 index 000000000000..eb159921a407 --- /dev/null +++ b/packages/utils/src/bls.ts @@ -0,0 +1,19 @@ +import {Signature, CoordType} from "@chainsafe/blst"; + +/** + * De-serialize bytes into Signature. + * No need to verify Signature is valid, already run sig-verify = false + */ +export function signatureFromBytesNoCheck(signature: Uint8Array): Signature { + return Signature.deserialize(signature, CoordType.affine); +} + +/** + * De-serialize bytes into Signature. + * No need to verify Signature is valid, already run sig-verify = false + */ +export function signatureFromBytes(signature: Uint8Array): Signature { + const sig = Signature.deserialize(signature, CoordType.affine); + sig.sigValidate(); + return sig; +} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 29e2cac2da21..4df044bb1cdb 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,6 +1,7 @@ export * from "./yaml/index.js"; export * from "./assert.js"; export * from "./base64.js"; +export * from "./bls.js"; export * from "./bytes.js"; export * from "./command.js"; export * from "./err.js"; diff --git a/packages/validator/package.json b/packages/validator/package.json index 3d13eca9c99e..d73458c32106 100644 --- a/packages/validator/package.json +++ b/packages/validator/package.json @@ -45,7 +45,7 @@ "blockchain" ], "dependencies": { - "@chainsafe/bls": "^8.1.0", + "@chainsafe/blst": "^1.0.1", "@chainsafe/ssz": "^0.15.1", "@lodestar/api": "^1.18.1", "@lodestar/config": "^1.18.1", diff --git a/packages/validator/src/services/externalSignerSync.ts b/packages/validator/src/services/externalSignerSync.ts index edfc0f5bc350..8ca1008224bd 100644 --- a/packages/validator/src/services/externalSignerSync.ts +++ b/packages/validator/src/services/externalSignerSync.ts @@ -1,5 +1,4 @@ -import bls from "@chainsafe/bls"; -import {CoordType} from "@chainsafe/bls/types"; +import {CoordType, PublicKey} from "@chainsafe/blst"; import {fromHexString} from "@chainsafe/ssz"; import {ChainForkConfig} from "@lodestar/config"; import {SLOTS_PER_EPOCH} from "@lodestar/params"; @@ -79,6 +78,6 @@ export function pollExternalSignerPubkeys( function assertValidPubkeysHex(pubkeysHex: string[]): void { for (const pubkeyHex of pubkeysHex) { const pubkeyBytes = fromHexString(pubkeyHex); - bls.PublicKey.fromBytes(pubkeyBytes, CoordType.jacobian, true); + PublicKey.deserialize(pubkeyBytes, CoordType.jacobian).keyValidate(); } } diff --git a/packages/validator/src/services/validatorStore.ts b/packages/validator/src/services/validatorStore.ts index 6cd9ed8dc065..9620a9026656 100644 --- a/packages/validator/src/services/validatorStore.ts +++ b/packages/validator/src/services/validatorStore.ts @@ -1,4 +1,4 @@ -import type {SecretKey} from "@chainsafe/bls/types"; +import {SecretKey} from "@chainsafe/blst"; import {BitArray, fromHexString, toHexString} from "@chainsafe/ssz"; import { computeEpochAtSlot, @@ -738,7 +738,7 @@ export class ValidatorStore { switch (signer.type) { case SignerType.Local: { const timer = this.metrics?.localSignTime.startTimer(); - const signature = signer.secretKey.sign(signingRoot).toBytes(); + const signature = signer.secretKey.sign(signingRoot).serialize(); timer?.(); return signature; } @@ -798,7 +798,7 @@ export class ValidatorStore { function getSignerPubkeyHex(signer: Signer): PubkeyHex { switch (signer.type) { case SignerType.Local: - return toHexString(signer.secretKey.toPublicKey().toBytes()); + return toHexString(signer.secretKey.toPublicKey().serialize()); case SignerType.Remote: if (!isValidatePubkeyHex(signer.pubkey)) { diff --git a/packages/validator/src/types.ts b/packages/validator/src/types.ts index 9a2314269234..dd44fd49c61e 100644 --- a/packages/validator/src/types.ts +++ b/packages/validator/src/types.ts @@ -1,4 +1,4 @@ -import type {SecretKey} from "@chainsafe/bls/types"; +import {SecretKey} from "@chainsafe/blst"; import {BLSPubkey} from "@lodestar/types"; import {DatabaseController} from "@lodestar/db"; diff --git a/packages/validator/test/e2e/web3signer.test.ts b/packages/validator/test/e2e/web3signer.test.ts index 326dbae8c4f7..2afb54d2881b 100644 --- a/packages/validator/test/e2e/web3signer.test.ts +++ b/packages/validator/test/e2e/web3signer.test.ts @@ -22,7 +22,7 @@ describe("web3signer signature test", function () { const subcommitteeIndex = 0; const secretKey = interopSecretKey(0); - const pubkeyBytes = secretKey.toPublicKey().toBytes(); + const pubkeyBytes = secretKey.toPublicKey().serialize(); let validatorStoreRemote: ValidatorStore; let validatorStoreLocal: ValidatorStore; diff --git a/packages/validator/test/unit/services/attestation.test.ts b/packages/validator/test/unit/services/attestation.test.ts index 397fef20b2ba..12f8d44bf8ec 100644 --- a/packages/validator/test/unit/services/attestation.test.ts +++ b/packages/validator/test/unit/services/attestation.test.ts @@ -1,5 +1,5 @@ import {describe, it, expect, beforeAll, beforeEach, afterEach, vi} from "vitest"; -import bls from "@chainsafe/bls"; +import {SecretKey} from "@chainsafe/blst"; import {toHexString} from "@chainsafe/ssz"; import {ssz} from "@lodestar/types"; import {HttpStatusCode, routes} from "@lodestar/api"; @@ -28,8 +28,8 @@ describe("AttestationService", function () { let pubkeys: Uint8Array[]; // Initialize pubkeys in before() so bls is already initialized beforeAll(() => { - const secretKeys = Array.from({length: 1}, (_, i) => bls.SecretKey.fromBytes(Buffer.alloc(32, i + 1))); - pubkeys = secretKeys.map((sk) => sk.toPublicKey().toBytes()); + const secretKeys = Array.from({length: 1}, (_, i) => SecretKey.deserialize(Buffer.alloc(32, i + 1))); + pubkeys = secretKeys.map((sk) => sk.toPublicKey().serialize()); validatorStore.votingPubkeys.mockReturnValue(pubkeys.map(toHexString)); validatorStore.hasVotingPubkey.mockReturnValue(true); validatorStore.hasSomeValidators.mockReturnValue(true); diff --git a/packages/validator/test/unit/services/attestationDuties.test.ts b/packages/validator/test/unit/services/attestationDuties.test.ts index 34924a4c3170..062102bdffc8 100644 --- a/packages/validator/test/unit/services/attestationDuties.test.ts +++ b/packages/validator/test/unit/services/attestationDuties.test.ts @@ -1,6 +1,6 @@ import {describe, it, expect, beforeAll, vi, Mocked, beforeEach, afterEach} from "vitest"; import {toBufferBE} from "bigint-buffer"; -import bls from "@chainsafe/bls"; +import {SecretKey} from "@chainsafe/blst"; import {toHexString} from "@chainsafe/ssz"; import {chainConfig} from "@lodestar/config/default"; import {HttpStatusCode, routes} from "@lodestar/api"; @@ -37,8 +37,8 @@ describe("AttestationDutiesService", function () { }; beforeAll(async () => { - const secretKeys = [bls.SecretKey.fromBytes(toBufferBE(BigInt(98), 32))]; - pubkeys = secretKeys.map((sk) => sk.toPublicKey().toBytes()); + const secretKeys = [SecretKey.deserialize(toBufferBE(BigInt(98), 32))]; + pubkeys = secretKeys.map((sk) => sk.toPublicKey().serialize()); validatorStore = await initValidatorStore(secretKeys, api, chainConfig); }); diff --git a/packages/validator/test/unit/services/block.test.ts b/packages/validator/test/unit/services/block.test.ts index bcfc57eb8674..7c5f617f3f24 100644 --- a/packages/validator/test/unit/services/block.test.ts +++ b/packages/validator/test/unit/services/block.test.ts @@ -1,5 +1,5 @@ import {describe, it, expect, beforeAll, beforeEach, afterEach, vi} from "vitest"; -import bls from "@chainsafe/bls"; +import {SecretKey} from "@chainsafe/blst"; import {toHexString} from "@chainsafe/ssz"; import {createChainForkConfig} from "@lodestar/config"; import {config as mainnetConfig} from "@lodestar/config/default"; @@ -25,8 +25,8 @@ describe("BlockDutiesService", function () { const config = createChainForkConfig(mainnetConfig); beforeAll(() => { - const secretKeys = Array.from({length: 2}, (_, i) => bls.SecretKey.fromBytes(Buffer.alloc(32, i + 1))); - pubkeys = secretKeys.map((sk) => sk.toPublicKey().toBytes()); + const secretKeys = Array.from({length: 2}, (_, i) => SecretKey.deserialize(Buffer.alloc(32, i + 1))); + pubkeys = secretKeys.map((sk) => sk.toPublicKey().serialize()); validatorStore.votingPubkeys.mockReturnValue(pubkeys.map(toHexString)); }); diff --git a/packages/validator/test/unit/services/blockDuties.test.ts b/packages/validator/test/unit/services/blockDuties.test.ts index 45dd99a80e77..f3c7194cd843 100644 --- a/packages/validator/test/unit/services/blockDuties.test.ts +++ b/packages/validator/test/unit/services/blockDuties.test.ts @@ -1,6 +1,6 @@ import {describe, it, expect, beforeAll, beforeEach, afterEach, vi} from "vitest"; import {toBufferBE} from "bigint-buffer"; -import bls from "@chainsafe/bls"; +import {SecretKey} from "@chainsafe/blst"; import {toHexString} from "@chainsafe/ssz"; import {RootHex} from "@lodestar/types"; import {HttpStatusCode, routes} from "@lodestar/api"; @@ -22,8 +22,8 @@ describe("BlockDutiesService", function () { let pubkeys: Uint8Array[]; // Initialize pubkeys in before() so bls is already initialized beforeAll(async () => { - const secretKeys = Array.from({length: 3}, (_, i) => bls.SecretKey.fromBytes(toBufferBE(BigInt(i + 1), 32))); - pubkeys = secretKeys.map((sk) => sk.toPublicKey().toBytes()); + const secretKeys = Array.from({length: 3}, (_, i) => SecretKey.deserialize(toBufferBE(BigInt(i + 1), 32))); + pubkeys = secretKeys.map((sk) => sk.toPublicKey().serialize()); validatorStore = await initValidatorStore(secretKeys, api); }); diff --git a/packages/validator/test/unit/services/externalSignerSync.test.ts b/packages/validator/test/unit/services/externalSignerSync.test.ts index 5fdf7d5ae5b2..2519fd0e06e8 100644 --- a/packages/validator/test/unit/services/externalSignerSync.test.ts +++ b/packages/validator/test/unit/services/externalSignerSync.test.ts @@ -1,7 +1,6 @@ import {MockedFunction, afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest"; import {toBufferBE} from "bigint-buffer"; -import bls from "@chainsafe/bls"; -import {SecretKey} from "@chainsafe/bls/types"; +import {SecretKey} from "@chainsafe/blst"; import {createChainForkConfig} from "@lodestar/config"; import {chainConfig} from "@lodestar/config/default"; import {ExternalSignerOptions, pollExternalSignerPubkeys} from "../../../src/services/externalSignerSync.js"; @@ -32,7 +31,7 @@ describe("External signer sync", () => { beforeAll(() => { vi.useFakeTimers(); - secretKeys = Array.from({length: 3}, (_, i) => bls.SecretKey.fromBytes(toBufferBE(BigInt(i + 1), 32))); + secretKeys = Array.from({length: 3}, (_, i) => SecretKey.deserialize(toBufferBE(BigInt(i + 1), 32))); pubkeys = secretKeys.map((sk) => sk.toPublicKey().toHex()); externalSignerGetKeysStub = vi.mocked(externalSignerGetKeys); }); diff --git a/packages/validator/test/unit/services/indicesService.test.ts b/packages/validator/test/unit/services/indicesService.test.ts index b94ec6fa398a..b80f19cc6359 100644 --- a/packages/validator/test/unit/services/indicesService.test.ts +++ b/packages/validator/test/unit/services/indicesService.test.ts @@ -1,6 +1,6 @@ import {describe, it, expect, beforeAll} from "vitest"; import {toBufferBE} from "bigint-buffer"; -import bls from "@chainsafe/bls"; +import {SecretKey} from "@chainsafe/blst"; import {toHexString} from "@chainsafe/ssz"; import {getApiClientStub} from "../../utils/apiStub.js"; import {testLogger} from "../../utils/logger.js"; @@ -14,10 +14,10 @@ describe("IndicesService", function () { beforeAll(() => { const secretKeys = [ - bls.SecretKey.fromBytes(toBufferBE(BigInt(98), 32)), - bls.SecretKey.fromBytes(toBufferBE(BigInt(99), 32)), + SecretKey.deserialize(toBufferBE(BigInt(98), 32)), + SecretKey.deserialize(toBufferBE(BigInt(99), 32)), ]; - pubkeys = secretKeys.map((sk) => sk.toPublicKey().toBytes()); + pubkeys = secretKeys.map((sk) => sk.toPublicKey().serialize()); }); it("Should remove pubkey", async function () { diff --git a/packages/validator/test/unit/services/syncCommitteDuties.test.ts b/packages/validator/test/unit/services/syncCommitteDuties.test.ts index 75e72cb7a36c..6091cc7d1a0c 100644 --- a/packages/validator/test/unit/services/syncCommitteDuties.test.ts +++ b/packages/validator/test/unit/services/syncCommitteDuties.test.ts @@ -1,7 +1,7 @@ import {describe, it, expect, beforeAll, beforeEach, afterEach} from "vitest"; import {when} from "vitest-when"; import {toBufferBE} from "bigint-buffer"; -import bls from "@chainsafe/bls"; +import {SecretKey} from "@chainsafe/blst"; import {toHexString} from "@chainsafe/ssz"; import {createChainForkConfig} from "@lodestar/config"; import {config as mainnetConfig} from "@lodestar/config/default"; @@ -43,10 +43,10 @@ describe("SyncCommitteeDutiesService", function () { beforeAll(async () => { const secretKeys = [ - bls.SecretKey.fromBytes(toBufferBE(BigInt(98), 32)), - bls.SecretKey.fromBytes(toBufferBE(BigInt(99), 32)), + SecretKey.deserialize(toBufferBE(BigInt(98), 32)), + SecretKey.deserialize(toBufferBE(BigInt(99), 32)), ]; - pubkeys = secretKeys.map((sk) => sk.toPublicKey().toBytes()); + pubkeys = secretKeys.map((sk) => sk.toPublicKey().serialize()); validatorStore = await initValidatorStore(secretKeys, api, altair0Config); }); diff --git a/packages/validator/test/unit/services/syncCommittee.test.ts b/packages/validator/test/unit/services/syncCommittee.test.ts index 57870da94dc3..6ebe2b35c2ac 100644 --- a/packages/validator/test/unit/services/syncCommittee.test.ts +++ b/packages/validator/test/unit/services/syncCommittee.test.ts @@ -1,5 +1,5 @@ import {describe, it, expect, beforeAll, beforeEach, afterEach, vi} from "vitest"; -import bls from "@chainsafe/bls"; +import {SecretKey} from "@chainsafe/blst"; import {toHexString} from "@chainsafe/ssz"; import {createChainForkConfig} from "@lodestar/config"; import {config as mainnetConfig} from "@lodestar/config/default"; @@ -37,8 +37,8 @@ describe("SyncCommitteeService", function () { }); beforeAll(() => { - const secretKeys = Array.from({length: 1}, (_, i) => bls.SecretKey.fromBytes(Buffer.alloc(32, i + 1))); - pubkeys = secretKeys.map((sk) => sk.toPublicKey().toBytes()); + const secretKeys = Array.from({length: 1}, (_, i) => SecretKey.deserialize(Buffer.alloc(32, i + 1))); + pubkeys = secretKeys.map((sk) => sk.toPublicKey().serialize()); validatorStore.votingPubkeys.mockReturnValue(pubkeys.map(toHexString)); validatorStore.hasVotingPubkey.mockReturnValue(true); validatorStore.hasSomeValidators.mockReturnValue(true); diff --git a/packages/validator/test/unit/validatorStore.test.ts b/packages/validator/test/unit/validatorStore.test.ts index 3f7f0792f378..135e940a5d9c 100644 --- a/packages/validator/test/unit/validatorStore.test.ts +++ b/packages/validator/test/unit/validatorStore.test.ts @@ -1,7 +1,7 @@ import {describe, it, expect, beforeEach, afterEach, vi} from "vitest"; import {toBufferBE} from "bigint-buffer"; -import bls from "@chainsafe/bls"; import {toHexString, fromHexString} from "@chainsafe/ssz"; +import {SecretKey} from "@chainsafe/blst"; import {chainConfig} from "@lodestar/config/default"; import {bellatrix} from "@lodestar/types"; import {routes} from "@lodestar/api"; @@ -92,8 +92,8 @@ describe("ValidatorStore", function () { }); }); -const secretKeys = Array.from({length: 3}, (_, i) => bls.SecretKey.fromBytes(toBufferBE(BigInt(i + 1), 32))); -const pubkeys = secretKeys.map((sk) => sk.toPublicKey().toBytes()); +const secretKeys = Array.from({length: 3}, (_, i) => SecretKey.deserialize(toBufferBE(BigInt(i + 1), 32))); +const pubkeys = secretKeys.map((sk) => sk.toPublicKey().serialize()); const valRegF00G100 = { message: { diff --git a/packages/validator/test/utils/validatorStore.ts b/packages/validator/test/utils/validatorStore.ts index 61de1e9371d6..4f24d54a3739 100644 --- a/packages/validator/test/utils/validatorStore.ts +++ b/packages/validator/test/utils/validatorStore.ts @@ -1,4 +1,4 @@ -import {SecretKey} from "@chainsafe/bls/types"; +import {SecretKey} from "@chainsafe/blst"; import {Api} from "@lodestar/api"; import {chainConfig} from "@lodestar/config/default"; import {createBeaconConfig, ChainConfig} from "@lodestar/config"; diff --git a/scripts/vite/plugins/blsBrowserPlugin.ts b/scripts/vite/plugins/blsBrowserPlugin.ts index 88c850f3fd1b..53e4ed0140c6 100644 --- a/scripts/vite/plugins/blsBrowserPlugin.ts +++ b/scripts/vite/plugins/blsBrowserPlugin.ts @@ -4,6 +4,7 @@ const __dirname = new URL(".", import.meta.url).pathname; const polyfillsDir = path.join(__dirname, "../polyfills"); const emptyModulePath = path.join(__dirname, "../polyfills/emptyModule.js"); +const emptyBlstModulePath = path.join(__dirname, "../polyfills/emptyBlstModule.js"); export function blsBrowserPlugin(): Plugin { return { @@ -19,7 +20,7 @@ export function blsBrowserPlugin(): Plugin { "@chainsafe/bls": "@chainsafe/bls/herumi", // This is just used to generate `privateKey` which is not used in the browser. "@chainsafe/bls-keygen": path.join(polyfillsDir, "bls-keygen.js"), - "@chainsafe/blst": emptyModulePath, + "@chainsafe/blst": emptyBlstModulePath, "@chainsafe/bls-hd-key": emptyModulePath, crypto: emptyModulePath, "node:crypto": emptyModulePath, diff --git a/scripts/vite/polyfills/emptyBlstModule.js b/scripts/vite/polyfills/emptyBlstModule.js new file mode 100644 index 000000000000..1005a2effe32 --- /dev/null +++ b/scripts/vite/polyfills/emptyBlstModule.js @@ -0,0 +1,2 @@ +export const Signature = {}; +export const CoordType = {}; diff --git a/vitest.base.browser.config.ts b/vitest.base.browser.config.ts index edd53c406ae1..53ae83b1d337 100644 --- a/vitest.base.browser.config.ts +++ b/vitest.base.browser.config.ts @@ -3,7 +3,7 @@ import {defineConfig} from "vitest/config"; const __dirname = new URL(".", import.meta.url).pathname; import {nodePolyfills} from "vite-plugin-node-polyfills"; import topLevelAwait from "vite-plugin-top-level-await"; -import { blsBrowserPlugin } from "./scripts/vite/plugins/blsBrowserPlugin"; +import {blsBrowserPlugin} from "./scripts/vite/plugins/blsBrowserPlugin.js"; export default defineConfig({ plugins: [ diff --git a/yarn.lock b/yarn.lock index 84d3e96408e9..1d183431b39c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -306,10 +306,10 @@ "@chainsafe/bls-keygen" "^0.4.0" bls-eth-wasm "^1.1.1" -"@chainsafe/blst@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@chainsafe/blst/-/blst-1.0.0.tgz#0939f5b38389ca8ae00fcc6b6555063113f611a7" - integrity sha512-WQDxFvcG+glmusXnWQ3W4g2tvHuTQEK0/cIZz37TskZJqOqAtOogZsK3cD3j+OoxbdCRT6l08QUCOtPMOLe/zA== +"@chainsafe/blst@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@chainsafe/blst/-/blst-1.0.1.tgz#e9bae59aaea9685d84c78846e958a467d5de6e9e" + integrity sha512-AURz1KWVg6x04ZfP2Ss+x6l8lX7cF/gXBsbCBId96Z1GiPjtU8CMnaVxY6jPZ6rojSozG/UiFyOTkiTiQRnrBA== dependencies: node-addon-api "^6.1.0" node-gyp "^10.0.1"