UNPKG

@lodestar/beacon-node

Version:

A Typescript implementation of the beacon chain

105 lines 4.2 kB
import worker from "node:worker_threads"; import { PublicKey } from "@chainsafe/blst"; import { expose } from "@chainsafe/threads/worker"; import { verifySignatureSetsMaybeBatch } from "../maybeBatch.js"; import { WorkResultCode } from "./types.js"; import { chunkifyMaximizeChunkSize } from "./utils.js"; /** * Split batchable sets in chunks of minimum size 16. * Batch verify 16 has an aprox cost of 16+1. For 32 it's 32+1. After ~16 the additional savings are not significant. * However, if a sig is invalid the whole batch has to be re-verified. So it's important to keep this number low. * In normal network conditions almost all signatures received by the node are correct. * After observing metrics this number can be reviewed */ const BATCHABLE_MIN_PER_CHUNK = 16; // Cloned data from instatiation const workerData = worker.workerData; if (!workerData) throw Error("workerData must be defined"); const { workerId } = workerData || {}; expose({ async verifyManySignatureSets(workReqArr) { return verifyManySignatureSets(workReqArr); }, }); function verifyManySignatureSets(workReqArr) { const [startSec, startNs] = process.hrtime(); const results = []; let batchRetries = 0; let batchSigsSuccess = 0; // If there are multiple batchable sets attempt batch verification with them const batchableSets = []; const nonBatchableSets = []; // 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 }); } else { nonBatchableSets.push({ idx: i, sets }); } } if (batchableSets.length > 0) { // Split batchable into chunks of max size ~ 32 to minimize cost if a sig is wrong const batchableChunks = chunkifyMaximizeChunkSize(batchableSets, BATCHABLE_MIN_PER_CHUNK); for (const batchableChunk of batchableChunks) { const allSets = []; for (const { sets } of batchableChunk) { for (const set of sets) { allSets.push(set); } } try { // Attempt to verify multiple sets at once const isValid = verifySignatureSetsMaybeBatch(allSets); if (isValid) { // The entire batch is valid, return success to all for (const { idx, sets } of batchableChunk) { batchSigsSuccess += sets.length; results[idx] = { code: WorkResultCode.success, result: isValid }; } } else { batchRetries++; // Re-verify all sigs nonBatchableSets.push(...batchableChunk); } } catch (_e) { // TODO: Ignore this error expecting that the same error will happen when re-verifying the set individually // It's not ideal but '@chainsafe/blst' may throw errors on some conditions batchRetries++; // Re-verify all sigs nonBatchableSets.push(...batchableChunk); } } } for (const { idx, sets } of nonBatchableSets) { try { const isValid = verifySignatureSetsMaybeBatch(sets); results[idx] = { code: WorkResultCode.success, result: isValid }; } catch (e) { results[idx] = { code: WorkResultCode.error, error: e }; } } const [workerEndSec, workerEndNs] = process.hrtime(); return { workerId, batchRetries, batchSigsSuccess, workerStartTime: [startSec, startNs], workerEndTime: [workerEndSec, workerEndNs], results, }; } function deserializeSet(set) { return { publicKey: PublicKey.fromBytes(set.publicKey), message: set.message, signature: set.signature, }; } //# sourceMappingURL=worker.js.map