From 36be94460c5d04bea8970997264a484b6723f555 Mon Sep 17 00:00:00 2001 From: matthewkeil Date: Thu, 20 Jun 2024 17:31:49 +0200 Subject: [PATCH] Revert "fix: revert napi bls (#6853)" This reverts commit cbb894688c6696886a5ef2acef589cfd190bbec0. --- .wordlist.txt | 1 + package.json | 4 +- packages/beacon-node/package.json | 3 +- packages/beacon-node/src/chain/bls/index.ts | 5 +- .../beacon-node/src/chain/bls/interface.ts | 2 +- .../chain/bls/{multithread => }/jobItem.ts | 26 +- .../beacon-node/src/chain/bls/maybeBatch.ts | 46 --- .../{multithread/index.ts => multiThread.ts} | 226 +++---------- .../src/chain/bls/multithread/poolSize.ts | 16 - .../src/chain/bls/multithread/utils.ts | 19 -- .../worker.ts => runBlsWorkReq.ts} | 48 +-- .../beacon-node/src/chain/bls/singleThread.ts | 19 +- .../src/chain/bls/{multithread => }/types.ts | 22 +- packages/beacon-node/src/chain/bls/utils.ts | 32 +- .../beacon-node/src/chain/bls/verifySets.ts | 58 ++++ packages/beacon-node/src/chain/chain.ts | 2 +- .../opPools/aggregatedAttestationPool.ts | 8 +- .../src/chain/opPools/attestationPool.ts | 14 +- .../chain/opPools/syncCommitteeMessagePool.ts | 11 +- .../opPools/syncContributionAndProofPool.ts | 9 +- .../beacon-node/src/chain/opPools/utils.ts | 10 - .../signatureSets/aggregateAndProof.ts | 2 +- .../signatureSets/selectionProof.ts | 2 +- .../syncCommitteeContribution.ts | 2 +- .../src/node/utils/interop/deposits.ts | 4 +- .../e2e/api/impl/lightclient/endpoint.test.ts | 4 +- .../test/e2e/chain/bls/multithread.test.ts | 13 +- packages/beacon-node/test/fixtures/capella.ts | 2 +- packages/beacon-node/test/mocks/mockedBls.ts | 2 +- .../perf/api/impl/validator/attester.test.ts | 3 +- .../beacon-node/test/perf/bls/bls.test.ts | 46 +-- .../produceBlock/produceBlockBody.test.ts | 2 +- packages/beacon-node/test/spec/bls/bls.ts | 68 ++-- packages/beacon-node/test/spec/general/bls.ts | 51 ++- .../test/unit/chain/bls/bls.test.ts | 23 +- .../test/unit/chain/bls/utils.test.ts | 2 +- .../test/unit/chain/genesis/genesis.test.ts | 6 +- .../opPools/aggregatedAttestationPool.test.ts | 18 +- .../unit/chain/opPools/syncCommittee.test.ts | 10 +- .../opPools/syncCommitteeContribution.test.ts | 11 +- ...idateGossipAttestationsSameAttData.test.ts | 9 +- .../validation/blsToExecutionChange.test.ts | 17 +- .../chain/validation/voluntaryExit.test.ts | 9 +- packages/beacon-node/test/utils/cache.ts | 4 +- .../beacon-node/test/utils/node/validator.ts | 2 +- packages/beacon-node/test/utils/state.ts | 6 +- packages/cli/package.json | 3 +- packages/cli/src/cmds/beacon/handler.ts | 1 + packages/cli/src/cmds/dev/files.ts | 2 +- .../cmds/validator/blsToExecutionChange.ts | 9 +- .../keymanager/decryptKeystoreDefinitions.ts | 4 +- .../cli/src/cmds/validator/keymanager/impl.ts | 4 +- .../validator/keymanager/keystoreCache.ts | 11 +- .../cli/src/cmds/validator/signers/index.ts | 6 +- .../cli/src/cmds/validator/voluntaryExit.ts | 8 +- packages/cli/src/index.ts | 12 +- packages/cli/src/options/globalOptions.ts | 7 + .../{applyPreset.ts => preInitialization.ts} | 40 +++ packages/cli/src/util/format.ts | 5 +- .../keymanager/keystoreCache.test.ts | 11 +- packages/cli/test/utils/cachedKeys.ts | 2 +- .../crucible/assertions/nodeAssertion.ts | 2 +- .../utils/crucible/externalSignerServer.ts | 2 +- .../cli/test/utils/crucible/interfaces.ts | 2 +- .../cli/test/utils/crucible/utils/keys.ts | 7 +- packages/flare/package.json | 2 +- packages/flare/src/cmds/selfSlashAttester.ts | 5 +- packages/flare/src/cmds/selfSlashProposer.ts | 4 +- packages/flare/src/util/deriveSecretKeys.ts | 5 +- packages/light-client/README.md | 6 + packages/light-client/package.json | 2 +- packages/prover/src/cli/index.ts | 2 +- .../{applyPreset.ts => preInitialization.ts} | 0 packages/state-transition/package.json | 3 +- .../src/block/processDeposit.ts | 12 +- .../state-transition/src/cache/epochCache.ts | 5 +- .../state-transition/src/cache/pubkeyCache.ts | 5 +- .../state-transition/src/cache/stateCache.ts | 5 +- .../src/epoch/processSyncCommitteeUpdates.ts | 4 +- .../src/signatureSets/blsToExecutionChange.ts | 11 +- packages/state-transition/src/util/interop.ts | 5 +- .../src/util/signatureSets.ts | 10 +- .../src/util/syncCommittee.ts | 4 +- .../state-transition/test/perf/block/util.ts | 8 +- packages/state-transition/test/perf/util.ts | 5 +- .../perf/util/loadState/loadState.test.ts | 5 +- .../test/unit/cachedBeaconState.test.ts | 2 +- .../test/unit/constants.test.ts | 10 - .../unit/signatureSets/signatureSets.test.ts | 6 +- .../state-transition/test/utils/interop.ts | 2 +- packages/test-utils/package.json | 2 +- packages/test-utils/src/keystores.ts | 4 +- packages/utils/package.json | 1 + packages/utils/src/bls.ts | 19 ++ packages/utils/src/index.ts | 1 + packages/validator/package.json | 2 +- .../src/services/externalSignerSync.ts | 5 +- .../validator/src/services/validatorStore.ts | 6 +- packages/validator/src/types.ts | 2 +- .../validator/test/e2e/web3signer.test.ts | 2 +- .../test/unit/services/attestation.test.ts | 6 +- .../unit/services/attestationDuties.test.ts | 6 +- .../test/unit/services/block.test.ts | 6 +- .../test/unit/services/blockDuties.test.ts | 6 +- .../unit/services/externalSignerSync.test.ts | 5 +- .../test/unit/services/indicesService.test.ts | 8 +- .../unit/services/syncCommitteDuties.test.ts | 8 +- .../test/unit/services/syncCommittee.test.ts | 6 +- .../test/unit/validatorStore.test.ts | 6 +- .../validator/test/utils/validatorStore.ts | 2 +- scripts/vite/plugins/blsBrowserPlugin.ts | 3 +- scripts/vite/polyfills/emptyBlstModule.js | 2 + vitest.base.browser.config.ts | 2 +- yarn.lock | 311 ++++++++---------- 114 files changed, 765 insertions(+), 821 deletions(-) rename packages/beacon-node/src/chain/bls/{multithread => }/jobItem.ts (78%) delete mode 100644 packages/beacon-node/src/chain/bls/maybeBatch.ts rename packages/beacon-node/src/chain/bls/{multithread/index.ts => multiThread.ts} (66%) delete mode 100644 packages/beacon-node/src/chain/bls/multithread/poolSize.ts delete mode 100644 packages/beacon-node/src/chain/bls/multithread/utils.ts rename packages/beacon-node/src/chain/bls/{multithread/worker.ts => runBlsWorkReq.ts} (63%) rename packages/beacon-node/src/chain/bls/{multithread => }/types.ts (69%) create mode 100644 packages/beacon-node/src/chain/bls/verifySets.ts rename packages/cli/src/{applyPreset.ts => preInitialization.ts} (63%) rename packages/prover/src/cli/{applyPreset.ts => preInitialization.ts} (100%) delete mode 100644 packages/state-transition/test/unit/constants.test.ts create mode 100644 packages/utils/src/bls.ts create mode 100644 scripts/vite/polyfills/emptyBlstModule.js diff --git a/.wordlist.txt b/.wordlist.txt index 46ca81441348..672261175ee8 100644 --- a/.wordlist.txt +++ b/.wordlist.txt @@ -171,6 +171,7 @@ orchestrator osx overriden params +peerDependency pid plaintext pre diff --git a/package.json b/package.json index 02e285e0c86a..d133230bea92 100644 --- a/package.json +++ b/package.json @@ -37,8 +37,8 @@ "test-coverage:e2e": "c8 --config .c8rc.json --report-dir coverage/e2e/ --all npm run test:e2e", "test-coverage:e2e-sim": "c8 --config .c8rc.json --report-dir coverage/e2e-sim/ --all npm run test:e2e:sim", "test-coverage:spec": "c8 --config .c8rc.json --report-dir coverage/spec/ --all npm run test:spec", - "benchmark": "yarn benchmark:files 'packages/*/test/perf/**/*.test.ts'", - "benchmark:files": "NODE_OPTIONS='--max-old-space-size=4096 --loader=ts-node/esm' benchmark --config .benchrc.yaml --defaultBranch unstable", + "benchmark": "UV_THREADPOOL_SIZE=$(node -e 'console.log(require(`os`).availableParallelism())') yarn benchmark:files 'packages/*/test/perf/**/*.test.ts'", + "benchmark:files": "UV_THREADPOOL_SIZE=$(node -e 'console.log(require(`os`).availableParallelism())') NODE_OPTIONS='--max-old-space-size=4096 --loader=ts-node/esm' benchmark --config .benchrc.yaml --defaultBranch unstable", "release:create-rc": "node scripts/release/create_rc.mjs", "release:tag-rc": "node scripts/release/tag_rc.mjs", "release:tag-stable": "node scripts/release/tag_stable.mjs", diff --git a/packages/beacon-node/package.json b/packages/beacon-node/package.json index ee17ff0c0c08..2016fd9e2e27 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": "7.1.3", - "@chainsafe/blst": "^0.2.11", + "@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/index.ts b/packages/beacon-node/src/chain/bls/index.ts index f9898b13776b..e80a3f8e13f6 100644 --- a/packages/beacon-node/src/chain/bls/index.ts +++ b/packages/beacon-node/src/chain/bls/index.ts @@ -1,4 +1,5 @@ export type {IBlsVerifier} from "./interface.js"; -export type {BlsMultiThreadWorkerPoolModules, JobQueueItemType} from "./multithread/index.js"; -export {BlsMultiThreadWorkerPool} from "./multithread/index.js"; +export type {JobQueueItemType} from "./jobItem.js"; +export type {BlsMultiThreadWorkerPoolModules} from "./multiThread.js"; +export {BlsMultiThreadWorkerPool} from "./multiThread.js"; export {BlsSingleThreadVerifier} from "./singleThread.js"; 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/multithread/jobItem.ts b/packages/beacon-node/src/chain/bls/jobItem.ts similarity index 78% rename from packages/beacon-node/src/chain/bls/multithread/jobItem.ts rename to packages/beacon-node/src/chain/bls/jobItem.ts index 8b5c63df2eeb..a67a10bf2a4d 100644 --- a/packages/beacon-node/src/chain/bls/multithread/jobItem.ts +++ b/packages/beacon-node/src/chain/bls/jobItem.ts @@ -1,10 +1,10 @@ -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 {VerifySignatureOpts} from "../interface.js"; -import {getAggregatedPubkey} from "../utils.js"; -import {LinkedList} from "../../../util/array.js"; -import {Metrics} from "../../../metrics/metrics.js"; +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"; export type JobQueueItem = JobQueueItemDefault | JobQueueItemSameMessage; @@ -49,14 +49,14 @@ 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 { opts: job.opts, sets: job.sets.map((set) => ({ // this can throw, handled in the consumer code - publicKey: getAggregatedPubkey(set, metrics).toBytes(format), + publicKey: getAggregatedPubkey(set, metrics), signature: set.signature, message: set.signingRoot, })), @@ -70,16 +70,20 @@ 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?.(); + const randomness: Uint8Array[] = []; + for (let i = 0; i < job.sets.length; i++) { + randomness.push(randomBytesNonZero(8)); + } return { opts: job.opts, sets: [ { - publicKey: bls.PublicKey.aggregate(job.sets.map((set) => set.publicKey)).toBytes(format), - signature: bls.Signature.aggregate(signatures).toBytes(format), message: job.message, + 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/maybeBatch.ts b/packages/beacon-node/src/chain/bls/maybeBatch.ts deleted file mode 100644 index 619ddf4d72ec..000000000000 --- a/packages/beacon-node/src/chain/bls/maybeBatch.ts +++ /dev/null @@ -1,46 +0,0 @@ -import {CoordType, PublicKey} from "@chainsafe/bls/types"; -import bls from "@chainsafe/bls"; - -const MIN_SET_COUNT_TO_BATCH = 2; - -export type SignatureSetDeserialized = { - publicKey: PublicKey; - message: Uint8Array; - signature: Uint8Array; -}; - -/** - * Verify signatures sets with batch verification or regular core verify depending on the set count. - * Abstracted in a separate file to be consumed by the threaded pool and the main thread implementation. - */ -export function verifySignatureSetsMaybeBatch(sets: SignatureSetDeserialized[]): boolean { - try { - if (sets.length >= MIN_SET_COUNT_TO_BATCH) { - return bls.Signature.verifyMultipleSignatures( - sets.map((s) => ({ - publicKey: s.publicKey, - message: s.message, - // true = validate signature - signature: bls.Signature.fromBytes(s.signature, CoordType.affine, true), - })) - ); - } - - // .every on an empty array returns true - if (sets.length === 0) { - throw Error("Empty signature set"); - } - - // If too few signature sets verify them without batching - return sets.every((set) => { - // true = validate signature - const sig = bls.Signature.fromBytes(set.signature, CoordType.affine, true); - return sig.verify(set.publicKey, set.message); - }); - } 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 - // see https://github.com/ChainSafe/blst-ts/blob/b1ba6333f664b08e5c50b2b0d18c4f079203962b/src/lib.ts#L291 - return false; - } -} diff --git a/packages/beacon-node/src/chain/bls/multithread/index.ts b/packages/beacon-node/src/chain/bls/multiThread.ts similarity index 66% rename from packages/beacon-node/src/chain/bls/multithread/index.ts rename to packages/beacon-node/src/chain/bls/multiThread.ts index 23e6f1bb460b..67820d84e874 100644 --- a/packages/beacon-node/src/chain/bls/multithread/index.ts +++ b/packages/beacon-node/src/chain/bls/multiThread.ts @@ -1,26 +1,17 @@ -/* eslint-disable @typescript-eslint/strict-boolean-expressions */ -import path from "node:path"; -import {spawn, Worker} from "@chainsafe/threads"; -// `threads` library creates self global variable which breaks `timeout-abort-controller` https://github.com/jacobheun/timeout-abort-controller/issues/9 -// Don't add an eslint disable here as a reminder that this has to be fixed eventually -// eslint-disable-next-line -// @ts-ignore -// eslint-disable-next-line -self = undefined; -import bls from "@chainsafe/bls"; -import {Implementation, PointFormat, PublicKey} from "@chainsafe/bls/types"; +import os from "node:os"; +import {PublicKey} from "@chainsafe/blst"; import {Logger} from "@lodestar/utils"; import {ISignatureSet} from "@lodestar/state-transition"; -import {QueueError, QueueErrorCode} from "../../../util/queue/index.js"; -import {Metrics} from "../../../metrics/index.js"; -import {IBlsVerifier, VerifySignatureOpts} from "../interface.js"; -import {getAggregatedPubkey, getAggregatedPubkeysCount} from "../utils.js"; -import {verifySignatureSetsMaybeBatch} from "../maybeBatch.js"; -import {LinkedList} from "../../../util/array.js"; -import {callInNextEventLoop} from "../../../util/eventLoop.js"; -import {BlsWorkReq, BlsWorkResult, WorkerData, WorkResultCode, WorkResultError} from "./types.js"; +import {QueueError, QueueErrorCode} from "../../util/queue/index.js"; +import {Metrics} from "../../metrics/index.js"; +import {LinkedList} from "../../util/array.js"; +import {callInNextEventLoop} from "../../util/eventLoop.js"; +import {IBlsVerifier, VerifySignatureOpts} from "./interface.js"; +import {getAggregatedPubkey, getAggregatedPubkeysCount, getJobResultError} from "./utils.js"; +import {verifySets} from "./verifySets.js"; +import {BlsWorkReq, WorkResultCode} from "./types.js"; import {chunkifyMaximizeChunkSize} from "./utils.js"; -import {defaultPoolSize} from "./poolSize.js"; +import {runBlsWorkReq} from "./runBlsWorkReq.js"; import { JobQueueItem, JobQueueItemSameMessage, @@ -30,9 +21,6 @@ import { jobItemWorkReq, } from "./jobItem.js"; -// Worker constructor consider the path relative to the current working directory -const workerDir = process.env.NODE_ENV === "test" ? "../../../../lib/chain/bls/multithread" : "./"; - export type BlsMultiThreadWorkerPoolModules = { logger: Logger; metrics: Metrics | null; @@ -42,11 +30,6 @@ export type BlsMultiThreadWorkerPoolOptions = { blsVerifyAllMultiThread?: boolean; }; -export type {JobQueueItemType}; - -// 1 worker for the main thread -const blsPoolSize = Math.max(defaultPoolSize - 1, 1); - /** * Split big signature sets into smaller sets so they can be sent to multiple workers. * @@ -80,30 +63,6 @@ const MAX_BUFFER_WAIT_MS = 100; */ const MAX_JOBS_CAN_ACCEPT_WORK = 512; -type WorkerApi = { - verifyManySignatureSets(workReqArr: BlsWorkReq[]): Promise; -}; - -enum WorkerStatusCode { - notInitialized, - initializing, - initializationError, - idle, - running, -} - -type WorkerStatus = - | {code: WorkerStatusCode.notInitialized} - | {code: WorkerStatusCode.initializing; initPromise: Promise} - | {code: WorkerStatusCode.initializationError; error: Error} - | {code: WorkerStatusCode.idle; workerApi: WorkerApi} - | {code: WorkerStatusCode.running; workerApi: WorkerApi}; - -type WorkerDescriptor = { - worker: Worker; - status: WorkerStatus; -}; - /** * Wraps "threads" library thread pool queue system with the goals: * - Complete total outstanding jobs in total minimum time possible. @@ -116,8 +75,9 @@ export class BlsMultiThreadWorkerPool implements IBlsVerifier { private readonly logger: Logger; private readonly metrics: Metrics | null; - private readonly format: PointFormat; - private readonly workers: WorkerDescriptor[]; + private blsPoolSize: number; + private workersBusy = 0; + private readonly jobs = new LinkedList(); private bufferedJobs: { jobs: LinkedList; @@ -128,34 +88,39 @@ export class BlsMultiThreadWorkerPool implements IBlsVerifier { } | null = null; private blsVerifyAllMultiThread: boolean; private closed = false; - private workersBusy = 0; constructor(options: BlsMultiThreadWorkerPoolOptions, modules: BlsMultiThreadWorkerPoolModules) { - const {logger, metrics} = modules; - this.logger = logger; - this.metrics = metrics; + this.logger = modules.logger; this.blsVerifyAllMultiThread = options.blsVerifyAllMultiThread ?? false; - // TODO: Allow to customize implementation - const implementation = bls.implementation; - - // Use compressed for herumi for now. - // THe worker is not able to deserialize from uncompressed - // `Error: err _wrapDeserialize` - this.format = implementation === "blst-native" ? PointFormat.uncompressed : PointFormat.compressed; - this.workers = this.createWorkers(implementation, blsPoolSize); + this.blsPoolSize = Number(process.env.UV_THREADPOOL_SIZE); + const defaultThreadpoolSize = os.availableParallelism(); + this.logger.info(`BLS libuv pool size: ${this.blsPoolSize}`); + /** + * Help users ensure that thread pool is large enough for optimal performance + * + * Node reports available CPUs. There is enough idle time on the main and + * network threads that setting UV_THREADPOOL_SIZE to $(nproc) provides the + * best performance. Recommend this value to consumers + */ + if (this.blsPoolSize < defaultThreadpoolSize) { + this.logger.warn( + `UV_THREADPOOL_SIZE=${this.blsPoolSize} which is less than available CPUs: ${defaultThreadpoolSize}. This will cause performance degradation.` + ); + } + const {metrics} = modules; + this.metrics = metrics; if (metrics) { metrics.blsThreadPool.queueLength.addCollect(() => { metrics.blsThreadPool.queueLength.set(this.jobs.length); - metrics.blsThreadPool.workersBusy.set(this.workersBusy); }); } } canAcceptWork(): boolean { return ( - this.workersBusy < blsPoolSize && + this.workersBusy < this.blsPoolSize && // TODO: Should also bound the jobs queue? this.jobs.length < MAX_JOBS_CAN_ACCEPT_WORK ); @@ -174,17 +139,15 @@ export class BlsMultiThreadWorkerPool implements IBlsVerifier { if (opts.verifyOnMainThread && !this.blsVerifyAllMultiThread) { const timer = this.metrics?.blsThreadPool.mainThreadDurationInThreadPool.startTimer(); - try { - return verifySignatureSetsMaybeBatch( - sets.map((set) => ({ - publicKey: getAggregatedPubkey(set), - message: set.signingRoot.valueOf(), - signature: set.signature, - })) - ); - } finally { - if (timer) timer(); - } + const isValid = verifySets( + sets.map((set) => ({ + publicKey: getAggregatedPubkey(set), + message: set.signingRoot.valueOf(), + signature: set.signature, + })) + ); + timer?.(); + return isValid; } // Split large array of sets into smaller. @@ -251,56 +214,8 @@ export class BlsMultiThreadWorkerPool implements IBlsVerifier { for (const job of this.jobs) { job.reject(new QueueError({code: QueueErrorCode.QUEUE_ABORTED})); } - this.jobs.clear(); - // Terminate all workers. await to ensure no workers are left hanging - await Promise.all( - Array.from(this.workers.entries()).map(([id, worker]) => - // NOTE: 'threads' has not yet updated types, and NodeJS complains with - // [DEP0132] DeprecationWarning: Passing a callback to worker.terminate() is deprecated. It returns a Promise instead. - (worker.worker.terminate() as unknown as Promise).catch((e: Error) => { - this.logger.error("Error terminating worker", {id}, e); - }) - ) - ); - } - - private createWorkers(implementation: Implementation, poolSize: number): WorkerDescriptor[] { - const workers: WorkerDescriptor[] = []; - - for (let i = 0; i < poolSize; i++) { - const workerData: WorkerData = {implementation, workerId: i}; - const worker = new Worker(path.join(workerDir, "worker.js"), { - workerData, - } as ConstructorParameters[1]); - - const workerDescriptor: WorkerDescriptor = { - worker, - status: {code: WorkerStatusCode.notInitialized}, - }; - workers.push(workerDescriptor); - - // TODO: Consider initializing only when necessary - const initPromise = spawn(worker, { - // A Lodestar Node may do very expensive task at start blocking the event loop and causing - // the initialization to timeout. The number below is big enough to almost disable the timeout - timeout: 5 * 60 * 1000, - }); - - workerDescriptor.status = {code: WorkerStatusCode.initializing, initPromise}; - - initPromise - .then((workerApi) => { - workerDescriptor.status = {code: WorkerStatusCode.idle, workerApi}; - // Potentially run jobs that were queued before initialization of the first worker - setTimeout(this.runJob, 0); - }) - .catch((error: Error) => { - workerDescriptor.status = {code: WorkerStatusCode.initializationError, error}; - }); - } - - return workers; + this.jobs.clear(); } /** @@ -311,18 +226,6 @@ export class BlsMultiThreadWorkerPool implements IBlsVerifier { throw new QueueError({code: QueueErrorCode.QUEUE_ABORTED}); } - // TODO: Consider if limiting queue size is necessary here. - // It would be bad to reject signatures because the node is slow. - // However, if the worker communication broke jobs won't ever finish - - if ( - this.workers.length > 0 && - this.workers[0].status.code === WorkerStatusCode.initializationError && - this.workers.every((worker) => worker.status.code === WorkerStatusCode.initializationError) - ) { - return job.reject(this.workers[0].status.error); - } - // Append batchable sets to `bufferedJobs`, starting a timeout to push them into `jobs`. // Do not call `runJob()`, it is called from `runBufferedJobs()` if (job.opts.batchable) { @@ -365,24 +268,12 @@ export class BlsMultiThreadWorkerPool implements IBlsVerifier { return; } - // Find idle worker - const worker = this.workers.find((worker) => worker.status.code === WorkerStatusCode.idle); - if (!worker || worker.status.code !== WorkerStatusCode.idle) { - return; - } - // Prepare work package const jobsInput = this.prepareWork(); if (jobsInput.length === 0) { return; } - // TODO: After sending the work to the worker the main thread can drop the job arguments - // and free-up memory, only needs to keep the job's Promise handlers. - // Maybe it's not useful since all data referenced in jobs is likely referenced by others - - const workerApi = worker.status.workerApi; - worker.status = {code: WorkerStatusCode.running, workerApi}; this.workersBusy++; try { @@ -400,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}); @@ -442,9 +333,9 @@ export class BlsMultiThreadWorkerPool implements IBlsVerifier { // Only downside is the job promise may be resolved twice, but that's not an issue const [jobStartSec, jobStartNs] = process.hrtime(); - const workResult = await workerApi.verifyManySignatureSets(workReqs); + const workResult = await runBlsWorkReq(workReqs); const [jobEndSec, jobEndNs] = process.hrtime(); - const {workerId, batchRetries, batchSigsSuccess, workerStartTime, workerEndTime, results} = workResult; + const {batchRetries, batchSigsSuccess, workerStartTime, workerEndTime, results} = workResult; const [workerStartSec, workerStartNs] = workerStartTime; const [workerEndSec, workerEndNs] = workerEndTime; @@ -461,7 +352,7 @@ export class BlsMultiThreadWorkerPool implements IBlsVerifier { // TODO: enable exhaustive switch case checks lint rule switch (job.type) { case JobQueueItemType.default: - if (!jobResult || jobResult.code !== WorkResultCode.success) { + if (jobResult.code !== WorkResultCode.success) { job.reject(getJobResultError(jobResult, i)); errorCount += sigSetCount; } else { @@ -472,7 +363,7 @@ export class BlsMultiThreadWorkerPool implements IBlsVerifier { // handle result of the verification of aggregated signature against aggregated pubkeys case JobQueueItemType.sameMessage: - if (!jobResult || jobResult.code !== WorkResultCode.success) { + if (jobResult.code !== WorkResultCode.success) { job.reject(getJobResultError(jobResult, i)); errorCount += 1; } else { @@ -489,12 +380,13 @@ export class BlsMultiThreadWorkerPool implements IBlsVerifier { } } + // TODO: (@matthewkeil) all of these metrics need to be revisited const workerJobTimeSec = workerEndSec - workerStartSec + (workerEndNs - workerStartNs) / 1e9; const latencyToWorkerSec = workerStartSec - jobStartSec + (workerStartNs - jobStartNs) / 1e9; const latencyFromWorkerSec = jobEndSec - workerEndSec + Number(jobEndNs - workerEndNs) / 1e9; this.metrics?.blsThreadPool.timePerSigSet.observe(workerJobTimeSec / startedSigSets); - this.metrics?.blsThreadPool.jobsWorkerTime.inc({workerId}, workerJobTimeSec); + this.metrics?.blsThreadPool.jobsWorkerTime.inc({workerId: 0}, workerJobTimeSec); this.metrics?.blsThreadPool.latencyToWorker.observe(latencyToWorkerSec); this.metrics?.blsThreadPool.latencyFromWorker.observe(latencyFromWorkerSec); this.metrics?.blsThreadPool.successJobsSignatureSetsCount.inc(successCount); @@ -512,7 +404,6 @@ export class BlsMultiThreadWorkerPool implements IBlsVerifier { } } - worker.status = {code: WorkerStatusCode.idle, workerApi}; this.workersBusy--; // Potentially run a new job @@ -567,21 +458,4 @@ export class BlsMultiThreadWorkerPool implements IBlsVerifier { this.metrics?.blsThreadPool.sameMessageRetryJobs.inc(1); this.metrics?.blsThreadPool.sameMessageRetrySets.inc(job.sets.length); } - - /** For testing */ - private async waitTillInitialized(): Promise { - await Promise.all( - this.workers.map(async (worker) => { - if (worker.status.code === WorkerStatusCode.initializing) { - await worker.status.initPromise; - } - }) - ); - } -} - -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; - return workerError; } diff --git a/packages/beacon-node/src/chain/bls/multithread/poolSize.ts b/packages/beacon-node/src/chain/bls/multithread/poolSize.ts deleted file mode 100644 index 9397f97849cd..000000000000 --- a/packages/beacon-node/src/chain/bls/multithread/poolSize.ts +++ /dev/null @@ -1,16 +0,0 @@ -let defaultPoolSize: number; - -try { - if (typeof navigator !== "undefined") { - defaultPoolSize = navigator.hardwareConcurrency ?? 4; - } else { - defaultPoolSize = (await import("node:os")).availableParallelism(); - } -} catch (e) { - defaultPoolSize = 8; -} - -/** - * Cross-platform aprox number of logical cores - */ -export {defaultPoolSize}; diff --git a/packages/beacon-node/src/chain/bls/multithread/utils.ts b/packages/beacon-node/src/chain/bls/multithread/utils.ts deleted file mode 100644 index 414decbb47bf..000000000000 --- a/packages/beacon-node/src/chain/bls/multithread/utils.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Splits an array into an array of arrays maximizing the size of the smallest chunk. - */ -export function chunkifyMaximizeChunkSize(arr: T[], minPerChunk: number): T[][] { - const chunkCount = Math.floor(arr.length / minPerChunk); - if (chunkCount <= 1) { - return [arr]; - } - - // Prefer less chunks of bigger size - const perChunk = Math.ceil(arr.length / chunkCount); - const arrArr: T[][] = []; - - for (let i = 0; i < arr.length; i += perChunk) { - arrArr.push(arr.slice(i, i + perChunk)); - } - - return arrArr; -} diff --git a/packages/beacon-node/src/chain/bls/multithread/worker.ts b/packages/beacon-node/src/chain/bls/runBlsWorkReq.ts similarity index 63% rename from packages/beacon-node/src/chain/bls/multithread/worker.ts rename to packages/beacon-node/src/chain/bls/runBlsWorkReq.ts index 0db88dcfccd8..580e97728cfa 100644 --- a/packages/beacon-node/src/chain/bls/multithread/worker.ts +++ b/packages/beacon-node/src/chain/bls/runBlsWorkReq.ts @@ -1,11 +1,6 @@ -/* eslint-disable @typescript-eslint/strict-boolean-expressions */ -import worker from "node:worker_threads"; -import {expose} from "@chainsafe/threads/worker"; -import bls from "@chainsafe/bls"; -import {CoordType} from "@chainsafe/bls/types"; -import {verifySignatureSetsMaybeBatch, SignatureSetDeserialized} from "../maybeBatch.js"; -import {WorkerData, BlsWorkReq, WorkResult, WorkResultCode, SerializedSet, BlsWorkResult} from "./types.js"; +import {asyncVerifySets} from "./verifySets.js"; import {chunkifyMaximizeChunkSize} from "./utils.js"; +import {BlsWorkReq, WorkResult, WorkResultCode, WorkRequestSet, BlsWorkResult} from "./types.js"; /** * Split batchable sets in chunks of minimum size 16. @@ -16,36 +11,23 @@ import {chunkifyMaximizeChunkSize} from "./utils.js"; */ const BATCHABLE_MIN_PER_CHUNK = 16; -// Cloned data from instatiation -const workerData = worker.workerData as WorkerData; -if (!workerData) throw Error("workerData must be defined"); -const {workerId} = workerData || {}; - -expose({ - async verifyManySignatureSets(workReqArr: BlsWorkReq[]): Promise { - return verifyManySignatureSets(workReqArr); - }, -}); - -function verifyManySignatureSets(workReqArr: BlsWorkReq[]): BlsWorkResult { +export async function runBlsWorkReq(workReqArr: BlsWorkReq[]): Promise { const [startSec, startNs] = process.hrtime(); const results: WorkResult[] = []; let batchRetries = 0; let batchSigsSuccess = 0; // If there are multiple batchable sets attempt batch verification with them - const batchableSets: {idx: number; sets: SignatureSetDeserialized[]}[] = []; - const nonBatchableSets: {idx: number; sets: SignatureSetDeserialized[]}[] = []; + const batchableSets: {idx: number; sets: WorkRequestSet[]}[] = []; + const nonBatchableSets: {idx: number; sets: WorkRequestSet[]}[] = []; // Split sets between batchable and non-batchable preserving their original index in the req array for (let i = 0; i < workReqArr.length; i++) { const workReq = workReqArr[i]; - const sets = workReq.sets.map(deserializeSet); - if (workReq.opts.batchable) { - batchableSets.push({idx: i, sets}); + batchableSets.push({idx: i, sets: workReq.sets}); } else { - nonBatchableSets.push({idx: i, sets}); + nonBatchableSets.push({idx: i, sets: workReq.sets}); } } @@ -54,7 +36,7 @@ function verifyManySignatureSets(workReqArr: BlsWorkReq[]): BlsWorkResult { const batchableChunks = chunkifyMaximizeChunkSize(batchableSets, BATCHABLE_MIN_PER_CHUNK); for (const batchableChunk of batchableChunks) { - const allSets: SignatureSetDeserialized[] = []; + const allSets: WorkRequestSet[] = []; for (const {sets} of batchableChunk) { for (const set of sets) { allSets.push(set); @@ -63,7 +45,7 @@ function verifyManySignatureSets(workReqArr: BlsWorkReq[]): BlsWorkResult { try { // Attempt to verify multiple sets at once - const isValid = verifySignatureSetsMaybeBatch(allSets); + const isValid = await asyncVerifySets(allSets); if (isValid) { // The entire batch is valid, return success to all @@ -88,7 +70,7 @@ function verifyManySignatureSets(workReqArr: BlsWorkReq[]): BlsWorkResult { for (const {idx, sets} of nonBatchableSets) { try { - const isValid = verifySignatureSetsMaybeBatch(sets); + const isValid = await asyncVerifySets(sets); results[idx] = {code: WorkResultCode.success, result: isValid}; } catch (e) { results[idx] = {code: WorkResultCode.error, error: e as Error}; @@ -97,8 +79,8 @@ function verifyManySignatureSets(workReqArr: BlsWorkReq[]): BlsWorkResult { const [workerEndSec, workerEndNs] = process.hrtime(); + // TODO: (@matthewkeil) all of these metrics need to be revisited return { - workerId, batchRetries, batchSigsSuccess, workerStartTime: [startSec, startNs], @@ -106,11 +88,3 @@ function verifyManySignatureSets(workReqArr: BlsWorkReq[]): BlsWorkResult { results, }; } - -function deserializeSet(set: SerializedSet): SignatureSetDeserialized { - return { - publicKey: bls.PublicKey.fromBytes(set.publicKey, CoordType.affine), - message: set.message, - signature: set.signature, - }; -} diff --git a/packages/beacon-node/src/chain/bls/singleThread.ts b/packages/beacon-node/src/chain/bls/singleThread.ts index 58ef6b6d9eec..7c80f0da89f9 100644 --- a/packages/beacon-node/src/chain/bls/singleThread.ts +++ b/packages/beacon-node/src/chain/bls/singleThread.ts @@ -1,10 +1,9 @@ -import {PublicKey, Signature} from "@chainsafe/bls/types"; -import bls from "@chainsafe/bls"; -import {CoordType} from "@chainsafe/blst"; +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 {verifySignatureSetsMaybeBatch} from "./maybeBatch.js"; +import {verifySets} from "./verifySets.js"; import {getAggregatedPubkey, getAggregatedPubkeysCount} from "./utils.js"; export class BlsSingleThreadVerifier implements IBlsVerifier { @@ -25,7 +24,7 @@ export class BlsSingleThreadVerifier implements IBlsVerifier { // Count time after aggregating const timer = this.metrics?.blsThreadPool.mainThreadDurationInThreadPool.startTimer(); - const isValid = verifySignatureSetsMaybeBatch(setsAggregated); + const isValid = verifySets(setsAggregated); // Don't use a try/catch, only count run without exceptions if (timer) { @@ -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/multithread/types.ts b/packages/beacon-node/src/chain/bls/types.ts similarity index 69% rename from packages/beacon-node/src/chain/bls/multithread/types.ts rename to packages/beacon-node/src/chain/bls/types.ts index 3ebc979b559e..3797f7df7385 100644 --- a/packages/beacon-node/src/chain/bls/multithread/types.ts +++ b/packages/beacon-node/src/chain/bls/types.ts @@ -1,19 +1,23 @@ -import {VerifySignatureOpts} from "../interface.js"; +import {PublicKey, Signature} from "@chainsafe/blst"; +import {VerifySignatureOpts} from "./interface.js"; -export type WorkerData = { - implementation: "herumi" | "blst-native"; - workerId: number; +export type DeserializedKeySet = { + publicKey: PublicKey; + message: Uint8Array; + signature: Uint8Array; }; -export type SerializedSet = { - publicKey: Uint8Array; +export type DeserializedSet = { + publicKey: PublicKey; message: Uint8Array; - signature: Uint8Array; + signature: Signature; }; +export type WorkRequestSet = DeserializedKeySet | DeserializedSet; + export type BlsWorkReq = { opts: VerifySignatureOpts; - sets: SerializedSet[]; + sets: WorkRequestSet[]; }; export enum WorkResultCode { @@ -25,8 +29,6 @@ export type WorkResultError = {code: WorkResultCode.error; error: Error}; export type WorkResult = {code: WorkResultCode.success; result: R} | WorkResultError; export type BlsWorkResult = { - /** Ascending integer identifying the worker for metrics */ - workerId: number; /** Total num of batches that had to be retried */ batchRetries: number; /** Total num of sigs that have been successfully verified with batching */ diff --git a/packages/beacon-node/src/chain/bls/utils.ts b/packages/beacon-node/src/chain/bls/utils.ts index 4a3a027f31ac..0c422709ec56 100644 --- a/packages/beacon-node/src/chain/bls/utils.ts +++ b/packages/beacon-node/src/chain/bls/utils.ts @@ -1,7 +1,7 @@ -import type {PublicKey} from "@chainsafe/bls/types"; -import bls from "@chainsafe/bls"; +import {PublicKey, aggregatePublicKeys} from "@chainsafe/blst"; import {ISignatureSet, SignatureSetType} from "@lodestar/state-transition"; import {Metrics} from "../../metrics/metrics.js"; +import {WorkResultError} from "./types.js"; export function getAggregatedPubkey(signatureSet: ISignatureSet, metrics: Metrics | null = null): PublicKey { switch (signatureSet.type) { @@ -10,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; } @@ -29,3 +29,29 @@ export function getAggregatedPubkeysCount(signatureSets: ISignatureSet[]): numbe } return pubkeysConut; } + +/** + * Splits an array into an array of arrays maximizing the size of the smallest chunk. + */ +export function chunkifyMaximizeChunkSize(arr: T[], minPerChunk: number): T[][] { + const chunkCount = Math.floor(arr.length / minPerChunk); + if (chunkCount <= 1) { + return [arr]; + } + + // Prefer less chunks of bigger size + const perChunk = Math.ceil(arr.length / chunkCount); + const arrArr: T[][] = []; + + for (let i = 0; i < arr.length; i += perChunk) { + arrArr.push(arr.slice(i, i + perChunk)); + } + + return arrArr; +} + +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; + return workerError; +} diff --git a/packages/beacon-node/src/chain/bls/verifySets.ts b/packages/beacon-node/src/chain/bls/verifySets.ts new file mode 100644 index 000000000000..d54d0bac5cf0 --- /dev/null +++ b/packages/beacon-node/src/chain/bls/verifySets.ts @@ -0,0 +1,58 @@ +import { + asyncVerify, + asyncVerifyMultipleAggregateSignatures, + verify, + verifyMultipleAggregateSignatures, +} from "@chainsafe/blst"; +import {WorkRequestSet} from "./types.js"; + +const MIN_SET_COUNT_TO_BATCH = 2; + +/** + * Verify signatures sets with batch verification or regular core verify depending on the set count. + * Abstracted in a separate file to be consumed by the threaded pool and the main thread implementation. + */ +export function verifySets(sets: WorkRequestSet[]): boolean { + try { + if (sets.length >= MIN_SET_COUNT_TO_BATCH) { + return verifyMultipleAggregateSignatures(sets); + } + + // .every on an empty array returns true + if (sets.length === 0) { + throw Error("Empty signature set"); + } + + // If too few signature sets verify them without batching + 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 + // see https://github.com/ChainSafe/blst-ts/blob/b1ba6333f664b08e5c50b2b0d18c4f079203962b/src/lib.ts#L291 + return false; + } +} + +export async function asyncVerifySets(sets: WorkRequestSet[]): Promise { + try { + if (sets.length >= MIN_SET_COUNT_TO_BATCH) { + return await asyncVerifyMultipleAggregateSignatures(sets); + } + + // .every on an empty array returns true + if (sets.length === 0) { + throw Error("Empty signature sets"); + } + + const promises = await Promise.all( + sets.map(({message, publicKey, signature}) => asyncVerify(message, publicKey, signature)) + ); + // If too few signature sets verify them without batching + return promises.every((isValid) => isValid); + } 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 + // see https://github.com/ChainSafe/blst-ts/blob/b1ba6333f664b08e5c50b2b0d18c4f079203962b/src/lib.ts#L291 + return false; + } +} diff --git a/packages/beacon-node/src/chain/chain.ts b/packages/beacon-node/src/chain/chain.ts index 2f58962f3cc5..cfc45e185532 100644 --- a/packages/beacon-node/src/chain/chain.ts +++ b/packages/beacon-node/src/chain/chain.ts @@ -593,7 +593,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 0f13575a5ec4..5f15851f3ee9 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 {getClient, routes} from "@lodestar/api"; @@ -127,7 +127,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 committee hash since we requested for the first period expect(committeeRes.value()).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 bf1e73469433..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,8 +1,7 @@ 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/multithread/index.js"; +import {BlsMultiThreadWorkerPool} from "../../../../src/chain/bls/index.js"; import {testLogger} from "../../../utils/logger.js"; import {VerifySignatureOpts} from "../../../../src/chain/bls/interface.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(), }); } }); @@ -50,8 +49,6 @@ describe("chain / bls / multithread queue", function () { const pool = new BlsMultiThreadWorkerPool({}, {logger, metrics: null}); // await terminating all workers afterEachCallbacks.push(() => pool.close()); - // Wait until initialized - await pool["waitTillInitialized"](); return pool; } 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 7bf8c2f7252f..3919b01db6c8 100644 --- a/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts +++ b/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts @@ -65,7 +65,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 e5b844262632..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,16 +1,15 @@ -import bls from "@chainsafe/bls"; -import {CoordType} from "@chainsafe/blst"; -import {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/multithread/index.js"; +import {BlsMultiThreadWorkerPool} from "../../../../src/chain/bls/index.js"; 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()}), @@ -28,7 +27,7 @@ describe("BlsVerifier ", function () { type: SignatureSetType.single, pubkey: secretKey.toPublicKey(), signingRoot, - signature: secretKey.sign(signingRoot).toBytes(), + signature: secretKey.sign(signingRoot).serialize(), }; }); }); @@ -46,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); }); @@ -61,7 +62,7 @@ describe("BlsVerifier ", function () { sets = secretKeys.map((secretKey) => { return { publicKey: secretKey.toPublicKey(), - signature: secretKey.sign(signingRoot).toBytes(), + signature: secretKey.sign(signingRoot).serialize(), }; }); }); @@ -72,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/bls/utils.test.ts b/packages/beacon-node/test/unit/chain/bls/utils.test.ts index d492a36b4d56..de7dd766acd0 100644 --- a/packages/beacon-node/test/unit/chain/bls/utils.test.ts +++ b/packages/beacon-node/test/unit/chain/bls/utils.test.ts @@ -1,5 +1,5 @@ import {describe, it, expect} from "vitest"; -import {chunkifyMaximizeChunkSize} from "../../../../src/chain/bls/multithread/utils.js"; +import {chunkifyMaximizeChunkSize} from "../../../../src/chain/bls/utils.js"; import {linspace} from "../../../../src/util/numpy.js"; describe("chain / bls / utils / chunkifyMaximizeChunkSize", () => { 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 c686449a29f2..1c17f0c5ed46 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 af9ebd479340..473fc9563cc3 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 6a2f9d04644d..cc83e799b6e5 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -51,10 +51,9 @@ "blockchain" ], "dependencies": { - "@chainsafe/bls": "7.1.3", + "@chainsafe/blst": "^1.0.1", "@chainsafe/bls-keygen": "^0.4.0", "@chainsafe/bls-keystore": "^3.0.1", - "@chainsafe/blst": "^0.2.11", "@chainsafe/discv5": "^9.0.0", "@chainsafe/enr": "^3.0.0", "@chainsafe/persistent-merkle-tree": "^0.7.1", diff --git a/packages/cli/src/cmds/beacon/handler.ts b/packages/cli/src/cmds/beacon/handler.ts index e24089194185..a51a7f7405b0 100644 --- a/packages/cli/src/cmds/beacon/handler.ts +++ b/packages/cli/src/cmds/beacon/handler.ts @@ -52,6 +52,7 @@ export async function beaconHandler(args: BeaconArgs & GlobalArgs): Promise { 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 960662b108ea..6f37f7f70449 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"; @@ -68,8 +67,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: validator.index, @@ -82,7 +81,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(), }; ( 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 36ded66976b1..093bc98ab64b 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 { @@ -149,7 +149,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 a08f33eb6bad..3e05e9f3e044 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 {ApiClient, getClient} from "@lodestar/api"; import {ensure0xPrefix, YargsError, wrapError} from "../../util/index.js"; @@ -161,7 +161,7 @@ async function processVoluntaryExit( data: voluntaryExit, type: SignableMessageType.VOLUNTARY_EXIT, }); - signature = bls.Signature.fromHex(signatureHex); + signature = Signature.deserialize(fromHex(signatureHex)); break; } default: @@ -170,7 +170,7 @@ async function processVoluntaryExit( const signedVoluntaryExit: phase0.SignedVoluntaryExit = { message: voluntaryExit, - signature: signature.toBytes(), + signature: signature.serialize(), }; (await client.beacon.submitPoolVoluntaryExit({signedVoluntaryExit})).assertOk(); diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 5cdccbacfeec..eb98b4ca65a5 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,7 +1,15 @@ #!/usr/bin/env node -// MUST import first to apply preset from args and set ssz hasher -import "./applyPreset.js"; +/** + * MUST import first!! + * - applies preset + * - sets ssz hasher + * - sets libuv thread pool size + */ +import "./preInitialization.js"; +/** + * Everything else must be after!! + */ import {YargsError} from "./util/index.js"; import {getLodestarCli, yarg} from "./cli.js"; import "source-map-support/register.js"; diff --git a/packages/cli/src/options/globalOptions.ts b/packages/cli/src/options/globalOptions.ts index 52a5090c6794..4bc11aff0eb7 100644 --- a/packages/cli/src/options/globalOptions.ts +++ b/packages/cli/src/options/globalOptions.ts @@ -10,6 +10,7 @@ type GlobalSingleArgs = { paramsFile?: string; preset: string; presetFile?: string; + uvThreadpoolSize?: number; }; export const defaultNetwork: NetworkName = "mainnet"; @@ -44,6 +45,12 @@ const globalSingleOptions: CliCommandOptions = { description: "Preset configuration file to override the active preset with custom values", type: "string", }, + + uvThreadpoolSize: { + hidden: true, + description: "Set the number of worker threads libuv should create", + type: "number", + }, }; export const rcConfigOption: [string, string, (configPath: string) => Record] = [ diff --git a/packages/cli/src/applyPreset.ts b/packages/cli/src/preInitialization.ts similarity index 63% rename from packages/cli/src/applyPreset.ts rename to packages/cli/src/preInitialization.ts index 25f78b7d32ac..d15094ae00e0 100644 --- a/packages/cli/src/applyPreset.ts +++ b/packages/cli/src/preInitialization.ts @@ -1,4 +1,5 @@ // MUST import this file first before anything and not import any Lodestar code. +import os from "node:os"; // eslint-disable-next-line no-restricted-imports import {hasher} from "@chainsafe/persistent-merkle-tree/lib/hasher/as-sha256.js"; @@ -29,6 +30,7 @@ import {readFile} from "./util/file.js"; const network = valueOfArg("network"); const preset = valueOfArg("preset"); const presetFile = valueOfArg("presetFile"); +const uvThreadpoolSize = valueOfArg("uvThreadpoolSize"); // Apply preset flag if present if (preset) { @@ -61,6 +63,44 @@ else if (process.argv[2] === "dev") { setActivePreset(PresetName.minimal, {FIELD_ELEMENTS_PER_BLOB: 4096}); } +/** + * Sets the libuv thread pool size for worker threads. This is a critical + * component for effective node operations. Setting the environment variable + * must happen almost at the beginning of startup, BEFORE the worker pool is + * created by libuv. + * + * The trigger for creation of the libuv worker pool is scheduling async work + * that will queue for a worker. An example of things that can trigger that + * condition are async reading files from the OS. Some network operations and + * any native modules that utilize async work (like @chainsafe/blst-ts). + * + * Setting this value higher than the number of logical cores will not be a benefit + * because the kernel will need to do context switching to parallelize the work + * on a number of cores that is less than the number of requested threads. + * + * Setting this number lower than then number of cores will reduce the amount of + * bls work that can be concurrently done. Something like 70% of the work the + * cpu does to keep up with the chain is blst validation. + * + * There is a considerable amount of idle process time on both the main thread + * and network thread and setting this value to overlap that work will allow the + * kernel to utilize the idle time for additional bls verification. + * + * Empirical testing has shown that sizing the worker pool to be as large as + * the number of logical cores is optimal but this may change in the future. + */ +if (uvThreadpoolSize) { + process.env.UV_THREADPOOL_SIZE = uvThreadpoolSize; +} else if (process.env.UV_THREADPOOL_SIZE) { + /* no-op let user-set value carry through */ +} else { + process.env.UV_THREADPOOL_SIZE = os.availableParallelism().toString(); +} + +if (isNaN(parseInt(`${process.env.UV_THREADPOOL_SIZE}`))) { + throw new Error(`UV_THREADPOOL_SIZE=${process.env.UV_THREADPOOL_SIZE}, but must be set to a number`); +} + if (presetFile) { // Override the active preset with custom values from file // Do not modify the preset to use as a base by passing null diff --git a/packages/cli/src/util/format.ts b/packages/cli/src/util/format.ts index e673fa4d4408..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/blst"; +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 0f4d0126bb20..4ac25dbadbc6 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 69039accb182..40bc88f9d7d0 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 {AssertionResult, ValidatorClientKeys, Assertion, ValidatorClient} from "../interfaces.js"; import {arrayEquals} from "../utils/index.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 c406d5a72432..dc6290fc6309 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 {ApiClient} from "@lodestar/api"; import {ApiClient 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 54a0dbec51f5..dfd68851985f 100644 --- a/packages/flare/package.json +++ b/packages/flare/package.json @@ -58,7 +58,7 @@ "blockchain" ], "dependencies": { - "@chainsafe/bls": "7.1.3", + "@chainsafe/blst": "^1.0.1", "@chainsafe/bls-keygen": "^0.4.0", "@lodestar/api": "^1.19.0", "@lodestar/config": "^1.19.0", diff --git a/packages/flare/src/cmds/selfSlashAttester.ts b/packages/flare/src/cmds/selfSlashAttester.ts index 6c42372f45c7..5b17c73e077c 100644 --- a/packages/flare/src/cmds/selfSlashAttester.ts +++ b/packages/flare/src/cmds/selfSlashAttester.ts @@ -1,6 +1,5 @@ -import bls from "@chainsafe/bls"; -import type {SecretKey} from "@chainsafe/bls/types"; import {getClient} from "@lodestar/api"; +import {SecretKey, aggregateSignatures} from "@chainsafe/blst"; import {phase0, ssz} from "@lodestar/types"; import {config as chainConfig} from "@lodestar/config/default"; import {createBeaconConfig, BeaconConfig} from "@lodestar/config"; @@ -149,5 +148,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 0b71d5558af8..d99d9bf50a26 100644 --- a/packages/flare/src/cmds/selfSlashProposer.ts +++ b/packages/flare/src/cmds/selfSlashProposer.ts @@ -1,5 +1,5 @@ -import type {SecretKey} from "@chainsafe/bls/types"; import {getClient} from "@lodestar/api"; +import {SecretKey} from "@chainsafe/blst"; import {phase0, ssz} from "@lodestar/types"; import {config as chainConfig} from "@lodestar/config/default"; import {createBeaconConfig, BeaconConfig} from "@lodestar/config"; @@ -136,5 +136,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/light-client/README.md b/packages/light-client/README.md index dd00777f81b5..224b43cb2fe8 100644 --- a/packages/light-client/README.md +++ b/packages/light-client/README.md @@ -50,6 +50,12 @@ lodestar lightclient \ For this example we will assume there is a running beacon node at `https://lodestar-sepolia.chainsafe.io` +If you are running light-client on a server/node environment there is a faster version of bls that can help with performance. It is a peerDependency and needs to be installed separately by the consumer of this package. This was done so that for browser situations there is not a hard requirement for node-only code that will cause bundling errors. On startup, if running in a node environment, and `@chainsafe/blst` is installed the LightClient will automatically use the faster bls bindings. + +```sh +npm i -S @chainsafe/blst +``` + ```ts import {Lightclient, LightclientEvent} from "@lodestar/light-client"; import {LightClientRestTransport} from "@lodestar/light-client/transport"; diff --git a/packages/light-client/package.json b/packages/light-client/package.json index 4f79eeb61274..3297c896fac0 100644 --- a/packages/light-client/package.json +++ b/packages/light-client/package.json @@ -72,7 +72,7 @@ "check-readme": "typescript-docs-verifier" }, "dependencies": { - "@chainsafe/bls": "7.1.3", + "@chainsafe/bls": "^8.1.0", "@chainsafe/persistent-merkle-tree": "^0.7.1", "@chainsafe/ssz": "^0.15.1", "@lodestar/api": "^1.19.0", diff --git a/packages/prover/src/cli/index.ts b/packages/prover/src/cli/index.ts index 845831b32cb0..467fe6bf5d04 100644 --- a/packages/prover/src/cli/index.ts +++ b/packages/prover/src/cli/index.ts @@ -1,7 +1,7 @@ #!/usr/bin/env node // MUST import first to apply preset from args and set ssz hasher -import "./applyPreset.js"; +import "./preInitialization.js"; import {YargsError} from "../utils/errors.js"; import {getLodestarProverCli, yarg} from "./cli.js"; import "source-map-support/register.js"; diff --git a/packages/prover/src/cli/applyPreset.ts b/packages/prover/src/cli/preInitialization.ts similarity index 100% rename from packages/prover/src/cli/applyPreset.ts rename to packages/prover/src/cli/preInitialization.ts diff --git a/packages/state-transition/package.json b/packages/state-transition/package.json index 76b7f52325a3..76da307ec550 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": "7.1.3", - "@chainsafe/blst": "^0.2.11", + "@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 fdc343ff4ec7..0fcfda0078c9 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 2891cd3e6216..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(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/constants.test.ts b/packages/state-transition/test/unit/constants.test.ts deleted file mode 100644 index 5b8cc66da73a..000000000000 --- a/packages/state-transition/test/unit/constants.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import {describe, it, expect} from "vitest"; -import * as blst from "@chainsafe/blst"; -import {G2_POINT_AT_INFINITY} from "../../src/index.js"; - -describe("constants", () => { - it("G2_POINT_AT_INFINITY", () => { - const p2 = blst.Signature.fromBytes(G2_POINT_AT_INFINITY); - expect(p2.value.is_inf()).toBe(true); - }); -}); 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 1a39f4d6c7ca..59ea462bd749 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -57,7 +57,7 @@ "blockchain" ], "dependencies": { - "@chainsafe/bls": "7.1.3", + "@chainsafe/blst": "^1.0.1", "@chainsafe/bls-keystore": "^3.0.1", "@lodestar/params": "^1.19.0", "@lodestar/utils": "^1.19.0", 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 a0c46bb4994d..587bfc10852f 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 7bd1cdae0ac7..4a7fc597882e 100644 --- a/packages/validator/package.json +++ b/packages/validator/package.json @@ -45,7 +45,7 @@ "blockchain" ], "dependencies": { - "@chainsafe/bls": "7.1.3", + "@chainsafe/blst": "^1.0.1", "@chainsafe/ssz": "^0.15.1", "@lodestar/api": "^1.19.0", "@lodestar/config": "^1.19.0", 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 e1254d1c6a52..b76e42713e01 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 {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 f7154a3a174e..885d8b945c40 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 {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 6864e62906d1..956a2c06eea4 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 c1edcc955b2d..b6b1b65f5678 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 {routes} from "@lodestar/api"; import {chainConfig} from "@lodestar/config/default"; @@ -19,8 +19,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 c44360485d09..1cefa0e76076 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 b6cba32fc96b..d1ab42a8cd22 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 5fe530ea0cfe..608ce1c7be04 100644 --- a/packages/validator/test/utils/validatorStore.ts +++ b/packages/validator/test/utils/validatorStore.ts @@ -1,5 +1,5 @@ -import {SecretKey} from "@chainsafe/bls/types"; import {ApiClient} from "@lodestar/api"; +import {SecretKey} from "@chainsafe/blst"; import {chainConfig} from "@lodestar/config/default"; import {createBeaconConfig, ChainConfig} from "@lodestar/config"; import {Signer, SignerType, ValidatorStore} from "../../src/index.js"; 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 19f39c58ea4f..4fcbceed3b2b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -298,22 +298,22 @@ ethereum-cryptography "^2.0.0" uuid "^9.0.0" -"@chainsafe/bls@7.1.3": - version "7.1.3" - resolved "https://registry.yarnpkg.com/@chainsafe/bls/-/bls-7.1.3.tgz#8d488357b187a511cfb94c96eddc7aa9f62644a9" - integrity sha512-d21eYdWxDSb63n7nB+viD+3U4yJW8huiKRibJyh8X7btPLoXkvtmDf7geYyHVbKfLDgbuHkc+b48pfPQkUTLxA== +"@chainsafe/bls@^8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@chainsafe/bls/-/bls-8.1.0.tgz#d0680ce1e8c10461f7c4c1b7be228a968d77efcb" + integrity sha512-C4a1gUpFHYzJG7aaik4w+7QTeJ4pGCDTZ28YUwAWOFaWgAJSK+769ib6CNBu5Txb3PpvcC5GuhICQaDopIgEHg== dependencies: "@chainsafe/bls-keygen" "^0.4.0" - bls-eth-wasm "^0.4.8" + bls-eth-wasm "^1.1.1" -"@chainsafe/blst@^0.2.11": - version "0.2.11" - resolved "https://registry.yarnpkg.com/@chainsafe/blst/-/blst-0.2.11.tgz#5ec85cd663592819d1dc51127e75dfd834250e3d" - integrity sha512-URyOLq5GtxBoxibOnd2pgLydCy0UZzbiIIBcsRAvGxAsRzjZL04TsQfwRkz5aphU3a1ebeRoMmI/HHyMCiFSQg== +"@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: - "@types/tar" "^6.1.4" - node-fetch "^2.6.1" - node-gyp "^8.4.0" + node-addon-api "^6.1.0" + node-gyp "^10.0.1" + ts-node "^10.9.2" "@chainsafe/discv5@^9.0.0": version "9.0.0" @@ -1271,7 +1271,7 @@ rfdc "^1.3.0" yaml "^2.2.2" -"@gar/promisify@^1.0.1", "@gar/promisify@^1.1.3": +"@gar/promisify@^1.1.3": version "1.1.3" resolved "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== @@ -2013,13 +2013,16 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@npmcli/fs@^1.0.0": - version "1.1.1" - resolved "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz" - integrity sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ== +"@npmcli/agent@^2.0.0": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@npmcli/agent/-/agent-2.2.2.tgz#967604918e62f620a648c7975461c9c9e74fc5d5" + integrity sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og== dependencies: - "@gar/promisify" "^1.0.1" - semver "^7.3.5" + agent-base "^7.1.0" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.1" + lru-cache "^10.0.1" + socks-proxy-agent "^8.0.3" "@npmcli/fs@^2.1.0": version "2.1.0" @@ -2058,14 +2061,6 @@ npm-bundled "^3.0.0" npm-normalize-package-bin "^3.0.0" -"@npmcli/move-file@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" - integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== - dependencies: - mkdirp "^1.0.4" - rimraf "^3.0.2" - "@npmcli/move-file@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-2.0.0.tgz#417f585016081a0184cef3e38902cd917a9bbd02" @@ -2788,11 +2783,6 @@ dependencies: defer-to-connect "^2.0.1" -"@tootallnate/once@1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" - integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== - "@tootallnate/once@2": version "2.0.0" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" @@ -3587,11 +3577,16 @@ JSONStream@^1.3.5: jsonparse "^1.2.0" through ">=2.2.7 <3" -abbrev@1, abbrev@^1.0.0: +abbrev@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abbrev@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf" + integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ== + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz" @@ -3671,7 +3666,14 @@ agent-base@^7.0.2, agent-base@^7.1.0: dependencies: debug "^4.3.4" -agentkeepalive@^4.1.3, agentkeepalive@^4.2.1: +agent-base@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== + dependencies: + debug "^4.3.4" + +agentkeepalive@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717" integrity sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA== @@ -4262,10 +4264,10 @@ bl@^5.0.0: inherits "^2.0.4" readable-stream "^3.4.0" -bls-eth-wasm@^0.4.8: - version "0.4.8" - resolved "https://registry.npmjs.org/bls-eth-wasm/-/bls-eth-wasm-0.4.8.tgz" - integrity sha512-ye7+G6KFLb3i9xSrLASAoYqOUK5WLB6XA5DD8Sh0UQpZ3T999ylsYbFdoOJpmvTDuBuMi23Vy8Jm0pn/GF01CA== +bls-eth-wasm@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/bls-eth-wasm/-/bls-eth-wasm-1.2.1.tgz#85f165c17d8f16000f46695b56f72bf6af386825" + integrity sha512-hl4oBzZQmPGNb9Wt5GI+oEuHM6twGc5HzXCzNZMVLMMg+dltsOuvuioRyLolpDFbncC0BJbGPzP1ZTysUGkksw== bluebird@~3.4.1: version "3.4.7" @@ -4507,30 +4509,6 @@ cac@^6.7.14: resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== -cacache@^15.2.0: - version "15.3.0" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" - integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ== - dependencies: - "@npmcli/fs" "^1.0.0" - "@npmcli/move-file" "^1.0.1" - chownr "^2.0.0" - fs-minipass "^2.0.0" - glob "^7.1.4" - infer-owner "^1.0.4" - lru-cache "^6.0.0" - minipass "^3.1.1" - minipass-collect "^1.0.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.2" - mkdirp "^1.0.3" - p-map "^4.0.0" - promise-inflight "^1.0.1" - rimraf "^3.0.2" - ssri "^8.0.1" - tar "^6.0.2" - unique-filename "^1.1.1" - cacache@^16.1.0: version "16.1.1" resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.1.tgz#4e79fb91d3efffe0630d5ad32db55cc1b870669c" @@ -4574,6 +4552,24 @@ cacache@^17.0.0: tar "^6.1.11" unique-filename "^3.0.0" +cacache@^18.0.0: + version "18.0.2" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-18.0.2.tgz#fd527ea0f03a603be5c0da5805635f8eef00c60c" + integrity sha512-r3NU8h/P+4lVUHfeRw1dtgQYar3DZMm4/cm2bZgOvrFC/su7budSOeqh52VJIC4U4iG1WWwV6vRW0znqBvxNuw== + dependencies: + "@npmcli/fs" "^3.1.0" + fs-minipass "^3.0.0" + glob "^10.2.2" + lru-cache "^10.0.1" + minipass "^7.0.3" + minipass-collect "^2.0.1" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + p-map "^4.0.0" + ssri "^10.0.0" + tar "^6.1.11" + unique-filename "^3.0.0" + cacheable-lookup@^5.0.3: version "5.0.4" resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" @@ -5745,7 +5741,7 @@ enabled@2.0.x: resolved "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz" integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== -encoding@^0.1.12, encoding@^0.1.13: +encoding@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== @@ -7031,6 +7027,17 @@ glob@^10.2.2: minipass "^5.0.0 || ^6.0.2 || ^7.0.0" path-scurry "^1.10.1" +glob@^10.3.10: + version "10.3.12" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.12.tgz#3a65c363c2e9998d220338e88a5f6ac97302960b" + integrity sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.3.6" + minimatch "^9.0.1" + minipass "^7.0.4" + path-scurry "^1.10.2" + glob@^7.1.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -7328,15 +7335,6 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" -http-proxy-agent@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" - integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== - dependencies: - "@tootallnate/once" "1" - agent-base "6" - debug "4" - http-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" @@ -7400,18 +7398,18 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" -https-proxy-agent@^7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz#e2645b846b90e96c6e6f347fb5b2e41f1590b09b" - integrity sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA== +https-proxy-agent@^7.0.1, https-proxy-agent@^7.0.3, https-proxy-agent@^7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz#8e97b841a029ad8ddc8731f26595bad868cb4168" + integrity sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg== dependencies: agent-base "^7.0.2" debug "4" -https-proxy-agent@^7.0.3, https-proxy-agent@^7.0.4: - version "7.0.4" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz#8e97b841a029ad8ddc8731f26595bad868cb4168" - integrity sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg== +https-proxy-agent@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz#e2645b846b90e96c6e6f347fb5b2e41f1590b09b" + integrity sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA== dependencies: agent-base "^7.0.2" debug "4" @@ -8169,7 +8167,7 @@ jackspeak@^2.0.3: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" -jackspeak@^2.3.5: +jackspeak@^2.3.5, jackspeak@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8" integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== @@ -8807,7 +8805,7 @@ lowercase-keys@^3.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== -lru-cache@^10.1.0, "lru-cache@^9.1.1 || ^10.0.0": +lru-cache@^10.0.1, lru-cache@^10.1.0, lru-cache@^10.2.0, "lru-cache@^9.1.1 || ^10.0.0": version "10.2.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3" integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q== @@ -8917,27 +8915,22 @@ make-fetch-happen@^11.0.0, make-fetch-happen@^11.0.1, make-fetch-happen@^11.1.1: socks-proxy-agent "^7.0.0" ssri "^10.0.0" -make-fetch-happen@^9.1.0: - version "9.1.0" - resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz" - integrity sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg== +make-fetch-happen@^13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-13.0.0.tgz#705d6f6cbd7faecb8eac2432f551e49475bfedf0" + integrity sha512-7ThobcL8brtGo9CavByQrQi+23aIfgYU++wg4B87AIS8Rb2ZBt/MEaDqzA00Xwv/jUjAjYkLHjVolYuTLKda2A== dependencies: - agentkeepalive "^4.1.3" - cacache "^15.2.0" - http-cache-semantics "^4.1.0" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" + "@npmcli/agent" "^2.0.0" + cacache "^18.0.0" + http-cache-semantics "^4.1.1" is-lambda "^1.0.1" - lru-cache "^6.0.0" - minipass "^3.1.3" - minipass-collect "^1.0.2" - minipass-fetch "^1.3.2" + minipass "^7.0.2" + minipass-fetch "^3.0.0" minipass-flush "^1.0.5" minipass-pipeline "^1.2.4" - negotiator "^0.6.2" + negotiator "^0.6.3" promise-retry "^2.0.1" - socks-proxy-agent "^6.0.0" - ssri "^8.0.0" + ssri "^10.0.0" map-obj@^1.0.0: version "1.0.1" @@ -9190,16 +9183,12 @@ minipass-collect@^1.0.2: dependencies: minipass "^3.0.0" -minipass-fetch@^1.3.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.4.1.tgz#d75e0091daac1b0ffd7e9d41629faff7d0c1f1b6" - integrity sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw== +minipass-collect@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-2.0.1.tgz#1621bc77e12258a12c60d34e2276ec5c20680863" + integrity sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw== dependencies: - minipass "^3.1.0" - minipass-sized "^1.0.3" - minizlib "^2.0.0" - optionalDependencies: - encoding "^0.1.12" + minipass "^7.0.3" minipass-fetch@^2.0.3: version "2.1.0" @@ -9238,7 +9227,7 @@ minipass-json-stream@^1.0.1: jsonparse "^1.3.1" minipass "^3.0.0" -minipass-pipeline@^1.2.2, minipass-pipeline@^1.2.4: +minipass-pipeline@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== @@ -9252,7 +9241,7 @@ minipass-sized@^1.0.3: dependencies: minipass "^3.0.0" -minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3, minipass@^3.1.6: +minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6: version "3.3.6" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== @@ -9269,12 +9258,12 @@ minipass@^5.0.0: resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.2, minipass@^7.0.3, minipass@^7.0.4: version "7.0.4" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== -minizlib@^2.0.0, minizlib@^2.1.1, minizlib@^2.1.2: +minizlib@^2.1.1, minizlib@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== @@ -9466,7 +9455,7 @@ natural-compare@^1.4.0: resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -negotiator@^0.6.2, negotiator@^0.6.3: +negotiator@^0.6.3: version "0.6.3" resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== @@ -9496,6 +9485,11 @@ node-addon-api@^5.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762" integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA== +node-addon-api@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76" + integrity sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA== + node-domexception@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" @@ -9529,21 +9523,21 @@ node-gyp-build@^4.3.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== -node-gyp@^8.4.0: - version "8.4.1" - resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz" - integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== +node-gyp@^10.0.1: + version "10.1.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-10.1.0.tgz#75e6f223f2acb4026866c26a2ead6aab75a8ca7e" + integrity sha512-B4J5M1cABxPc5PwfjhbV5hoy2DP9p8lFXASnEN6hugXOa61416tnTZ29x9sSwAd0o99XNIcpvDDy1swAExsVKA== dependencies: env-paths "^2.2.0" - glob "^7.1.4" + exponential-backoff "^3.1.1" + glob "^10.3.10" graceful-fs "^4.2.6" - make-fetch-happen "^9.1.0" - nopt "^5.0.0" - npmlog "^6.0.0" - rimraf "^3.0.2" + make-fetch-happen "^13.0.0" + nopt "^7.0.0" + proc-log "^3.0.0" semver "^7.3.5" tar "^6.1.2" - which "^2.0.2" + which "^4.0.0" node-gyp@^9.0.0, node-gyp@^9.4.0: version "9.4.1" @@ -9629,13 +9623,6 @@ node-stdlib-browser@^1.2.0: util "^0.12.4" vm-browserify "^1.0.1" -nopt@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" - integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== - dependencies: - abbrev "1" - nopt@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/nopt/-/nopt-6.0.0.tgz#245801d8ebf409c6df22ab9d95b65e1309cdb16d" @@ -9643,6 +9630,13 @@ nopt@^6.0.0: dependencies: abbrev "^1.0.0" +nopt@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.0.tgz#067378c68116f602f552876194fd11f1292503d7" + integrity sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA== + dependencies: + abbrev "^2.0.0" + normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -10373,6 +10367,14 @@ path-scurry@^1.10.1, path-scurry@^1.6.1: lru-cache "^9.1.1 || ^10.0.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" +path-scurry@^1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.2.tgz#8f6357eb1239d5fa1da8b9f70e9c080675458ba7" + integrity sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-type@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" @@ -11513,15 +11515,6 @@ snappyjs@^0.7.0: resolved "https://registry.yarnpkg.com/snappyjs/-/snappyjs-0.7.0.tgz#6096eac06382700ae7fdefa579dea5e2aa20f51c" integrity sha512-u5iEEXkMe2EInQio6Wv9LWHOQYRDbD2O9hzS27GpT/lwfIQhTCnHCTqedqHIHe9ZcvQo+9au6vngQayipz1NYw== -socks-proxy-agent@^6.0.0: - version "6.1.1" - resolved "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz" - integrity sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew== - dependencies: - agent-base "^6.0.2" - debug "^4.3.1" - socks "^2.6.1" - socks-proxy-agent@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6" @@ -11540,7 +11533,16 @@ socks-proxy-agent@^8.0.2: debug "^4.3.4" socks "^2.7.1" -socks@^2.6.1, socks@^2.6.2, socks@^2.7.1: +socks-proxy-agent@^8.0.3: + version "8.0.3" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.3.tgz#6b2da3d77364fde6292e810b496cb70440b9b89d" + integrity sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A== + dependencies: + agent-base "^7.1.1" + debug "^4.3.4" + socks "^2.7.1" + +socks@^2.6.2, socks@^2.7.1: version "2.7.1" resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== @@ -11676,13 +11678,6 @@ ssri@^10.0.0, ssri@^10.0.1: dependencies: minipass "^4.0.0" -ssri@^8.0.0, ssri@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" - integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== - dependencies: - minipass "^3.1.1" - ssri@^9.0.0, ssri@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" @@ -11789,16 +11784,7 @@ string-argv@~0.3.1: resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -12089,7 +12075,7 @@ tar@6.1.11: mkdirp "^1.0.3" yallist "^4.0.0" -tar@^6.0.2, tar@^6.1.11, tar@^6.1.13, tar@^6.1.2: +tar@^6.1.11, tar@^6.1.13, tar@^6.1.2: version "6.2.1" resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== @@ -13433,16 +13419,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==