@obolnetwork/obol-sdk 2.12.1 → 2.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/dist/browser/src/index.js +77 -52
- package/dist/browser/src/index.js.map +1 -1
- package/dist/cjs/src/index.js +79 -52
- package/dist/cjs/src/index.js.map +1 -1
- package/dist/cjs/src/verification/clusterLockValidationWorker.js +2 -4
- package/dist/cjs/src/verification/clusterLockValidationWorker.js.map +1 -1
- package/dist/cjs/src/verification/parallelPool.js +8 -9
- package/dist/cjs/src/verification/parallelPool.js.map +1 -1
- package/dist/esm/src/{chunk-5ASVONSJ.js → chunk-T74RX3N2.js} +4 -6
- package/dist/esm/src/chunk-T74RX3N2.js.map +1 -0
- package/dist/esm/src/{chunk-RYTIXFRX.js → chunk-YSXFCSVK.js} +17 -10
- package/dist/esm/src/{chunk-RYTIXFRX.js.map → chunk-YSXFCSVK.js.map} +1 -1
- package/dist/esm/src/index.js +25 -3
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/verification/clusterLockValidationWorker.js +2 -2
- package/dist/esm/src/verification/parallelPool.js +1 -1
- package/dist/types/src/base.d.ts +5 -3
- package/dist/types/src/errors.d.ts +8 -0
- package/dist/types/src/index.d.ts +5 -6
- package/dist/types/test/client/baseUrl.spec.d.ts +1 -0
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/dist/esm/src/chunk-5ASVONSJ.js.map +0 -1
|
@@ -35,6 +35,13 @@ var UnsupportedChainError = class _UnsupportedChainError extends Error {
|
|
|
35
35
|
Object.setPrototypeOf(this, _UnsupportedChainError.prototype);
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
|
+
var InvalidBaseUrlError = class _InvalidBaseUrlError extends Error {
|
|
39
|
+
constructor(message) {
|
|
40
|
+
super(message);
|
|
41
|
+
this.name = "InvalidBaseUrlError";
|
|
42
|
+
Object.setPrototypeOf(this, _InvalidBaseUrlError.prototype);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
38
45
|
var ClusterLockValidationTimeoutError = class _ClusterLockValidationTimeoutError extends Error {
|
|
39
46
|
/**
|
|
40
47
|
* @param timeoutMs - Same value as SDK whole-lock validation worker timeout.
|
|
@@ -338,15 +345,7 @@ function validateClusterLockInWorker(lock, safeRpcUrl) {
|
|
|
338
345
|
const worker = new wt.Worker(workerFile, {
|
|
339
346
|
workerData: { lock, safeRpcUrl }
|
|
340
347
|
});
|
|
341
|
-
|
|
342
|
-
const finish = (result) => {
|
|
343
|
-
if (settled) return;
|
|
344
|
-
settled = true;
|
|
345
|
-
clearTimeout(timer);
|
|
346
|
-
worker.terminate();
|
|
347
|
-
resolve(result);
|
|
348
|
-
};
|
|
349
|
-
timer = setTimeout(() => {
|
|
348
|
+
const timer = setTimeout(() => {
|
|
350
349
|
if (settled) return;
|
|
351
350
|
settled = true;
|
|
352
351
|
clearTimeout(timer);
|
|
@@ -355,6 +354,13 @@ function validateClusterLockInWorker(lock, safeRpcUrl) {
|
|
|
355
354
|
new ClusterLockValidationTimeoutError(VALIDATION_WORKER_TIMEOUT_MS)
|
|
356
355
|
);
|
|
357
356
|
}, VALIDATION_WORKER_TIMEOUT_MS);
|
|
357
|
+
const finish = (result) => {
|
|
358
|
+
if (settled) return;
|
|
359
|
+
settled = true;
|
|
360
|
+
clearTimeout(timer);
|
|
361
|
+
worker.terminate();
|
|
362
|
+
resolve(result);
|
|
363
|
+
};
|
|
358
364
|
worker.once("message", (msg) => {
|
|
359
365
|
finish(msg === true);
|
|
360
366
|
});
|
|
@@ -372,6 +378,7 @@ export {
|
|
|
372
378
|
ConflictError,
|
|
373
379
|
SignerRequiredError,
|
|
374
380
|
UnsupportedChainError,
|
|
381
|
+
InvalidBaseUrlError,
|
|
375
382
|
ClusterLockValidationTimeoutError,
|
|
376
383
|
verifySharesBinding,
|
|
377
384
|
verifyBlsChecksParallel,
|
|
@@ -379,4 +386,4 @@ export {
|
|
|
379
386
|
verifyAggregateParallel,
|
|
380
387
|
validateClusterLockInWorker
|
|
381
388
|
};
|
|
382
|
-
//# sourceMappingURL=chunk-
|
|
389
|
+
//# sourceMappingURL=chunk-YSXFCSVK.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/verification/parallelPool.ts","../../../src/errors.ts"],"sourcesContent":["// Parallel BLS verification via Node worker_threads.\n//\n// IMPORTANT: only the CJS build actually runs in parallel. ESM Node and\n// browser bundles transparently fall back to sync. See the matrix in\n// PARALLEL_VALIDATION_PLAN.md. The main consumer flow (charon DKG ->\n// obol-api NestJS handler -> SDK CJS) is CJS, so it gets the speedup.\n//\n// Strategy:\n// - Look up the worker file via __dirname (CJS only). If undefined or the\n// file isn't where we expect (ESM, browser, source-mode tsx/jest), return\n// undefined and fall back to sync.\n// - Below MIN_PARALLEL_* thresholds, sync wins (worker spin-up dominates).\n// - Spawn one worker per chunk, await all, collapse to a single boolean.\n\nimport { fromHexString } from '@chainsafe/ssz';\nimport {\n blsAggregatePublicKeys,\n blsAggregateSignatures,\n blsRecoverDistributedPubkeyFromShares,\n blsVerifyAggregate,\n blsVerifyExtraShares,\n blsVerifyMultiple,\n blsVerifyWithAggregatedPubkey,\n} from '../blsUtils.js';\n// Type-only imports — erased at compile time, so they don't pull node:*\n// modules into the browser bundle.\nimport type * as WTNs from 'node:worker_threads';\nimport type * as OsNs from 'node:os';\nimport type * as PathNs from 'node:path';\nimport type * as FsNs from 'node:fs';\nimport { ClusterLockValidationTimeoutError } from '../errors.js';\nimport type { BlsSignatureCheck, ClusterLock, SafeRpcUrl } from '../types.js';\nimport type { AggregatePubkeysInput, WorkerInput } from './lockWorker.js';\n\nconst MIN_PARALLEL_VALIDATORS = 50;\nconst MIN_PARALLEL_BATCH_PAIRS = 100;\nconst MAX_WORKERS = 8;\nconst MIN_VALIDATORS_PER_WORKER = 25;\nconst MIN_PAIRS_PER_WORKER = 50;\nconst MIN_PARALLEL_AGGREGATE_KEYS = 400;\nconst MIN_KEYS_PER_WORKER_AGG = 100;\n// Hard ceiling so a stuck worker can't leak past obol-api's HTTP timeout.\n// Biggest legitimate batch (~1000 pairs) finishes in ~3s on the bench; 60s\n// is conservative enough to never false-trip on real input.\nconst MIN_VALIDATORS_FOR_VALIDATION_WORKER = 50;\nconst VALIDATION_WORKER_TIMEOUT_MS = 120_000;\nconst WORKER_TIMEOUT_MS = 60_000;\n// Cap simultaneous workers (memory). Scales up for large jobs on multi-core hosts.\nconst MAX_CONCURRENT_WORKERS_CAP = 8;\n\nfunction maxConcurrentWorkers(numWorkers: number): number {\n return Math.min(numWorkers, MAX_CONCURRENT_WORKERS_CAP);\n}\n\ntype WorkerThreads = typeof WTNs;\ntype NodeOs = typeof OsNs;\n\nlet workerThreadsCache: WorkerThreads | null | undefined;\nlet osCache: NodeOs | null | undefined;\n// Tri-state per worker file: undefined = uncached, null = unavailable, string = path.\nconst workerPathByFile = new Map<string, string | null | undefined>();\n\nfunction loadWorkerThreads(): WorkerThreads | null {\n if (workerThreadsCache !== undefined) return workerThreadsCache;\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires\n workerThreadsCache = require('node:worker_threads') as WorkerThreads;\n } catch {\n workerThreadsCache = null;\n }\n return workerThreadsCache;\n}\n\nfunction loadOs(): NodeOs | null {\n if (osCache !== undefined) return osCache;\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires\n osCache = require('node:os') as NodeOs;\n } catch {\n osCache = null;\n }\n return osCache;\n}\n\nfunction getWorkerPath(filename: string): string | null {\n const cached = workerPathByFile.get(filename);\n if (cached !== undefined) return cached;\n if (typeof __dirname === 'undefined') {\n workerPathByFile.set(filename, null);\n return null;\n }\n // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires\n const path = require('node:path') as typeof PathNs;\n // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires\n const fs = require('node:fs') as typeof FsNs;\n // Bundled index.js: __dirname is dist/cjs/src; workers live in verification/.\n // Standalone parallelPool.js entry: __dirname is already verification/.\n const candidates = [\n path.join(__dirname, filename),\n path.join(__dirname, 'verification', filename),\n ];\n try {\n for (const candidate of candidates) {\n if (fs.existsSync(candidate)) {\n workerPathByFile.set(filename, candidate);\n return candidate;\n }\n }\n } catch {\n // fall through\n }\n workerPathByFile.set(filename, null);\n return null;\n}\n\nasync function mapWithConcurrency<T, R>(\n items: T[],\n concurrency: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results: R[] = new Array(items.length);\n let next = 0;\n const runners = Array.from(\n { length: Math.min(concurrency, items.length) },\n async () => {\n while (next < items.length) {\n const i = next++;\n results[i] = await fn(items[i], i);\n }\n },\n );\n await Promise.all(runners);\n return results;\n}\n\nfunction chunkArrays<T>(arr: T[], n: number): T[][] {\n const size = Math.ceil(arr.length / n);\n const out: T[][] = [];\n for (let i = 0; i < arr.length; i += size) {\n out.push(arr.slice(i, i + size));\n }\n return out;\n}\n\nfunction verifySharesSync(\n shares: string[][],\n distributedKeys: string[],\n threshold: number,\n): boolean {\n for (let i = 0; i < shares.length; i++) {\n const sharesBytes = shares[i].map(s => fromHexString(s));\n const dkBytes = fromHexString(distributedKeys[i]);\n const recovered = blsRecoverDistributedPubkeyFromShares(\n sharesBytes,\n threshold,\n );\n if (!recovered) return false;\n if (recovered.length !== dkBytes.length) return false;\n for (let j = 0; j < recovered.length; j++) {\n if (recovered[j] !== dkBytes[j]) return false;\n }\n if (!blsVerifyExtraShares(sharesBytes, threshold, dkBytes)) {\n return false;\n }\n }\n return true;\n}\n\nasync function runWorkerAggregatePubkeys(\n wt: WorkerThreads,\n workerFile: string,\n data: AggregatePubkeysInput,\n): Promise<Uint8Array | null> {\n return await new Promise(resolve => {\n let settled = false;\n const worker = new wt.Worker(workerFile, { workerData: data });\n const finish = (result: Uint8Array | null): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n resolve(result);\n };\n const timer = setTimeout(() => {\n finish(null);\n }, WORKER_TIMEOUT_MS);\n worker.once('message', (msg: unknown) => {\n finish(msg instanceof Uint8Array ? msg : null);\n });\n worker.once('error', () => {\n finish(null);\n });\n worker.once('exit', code => {\n if (code !== 0) finish(null);\n });\n });\n}\n\nasync function runWorker(\n wt: WorkerThreads,\n workerFile: string,\n data: WorkerInput,\n): Promise<boolean> {\n return await new Promise(resolve => {\n let settled = false;\n const worker = new wt.Worker(workerFile, { workerData: data });\n const finish = (result: boolean): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n resolve(result);\n };\n const timer = setTimeout(() => {\n finish(false);\n }, WORKER_TIMEOUT_MS);\n worker.once('message', (msg: unknown) => {\n finish(msg === true);\n });\n worker.once('error', () => {\n finish(false);\n });\n worker.once('exit', code => {\n if (code !== 0) finish(false);\n });\n });\n}\n\nfunction poolSize(\n itemCount: number,\n minPerWorker: number,\n): {\n numWorkers: number;\n wt: WorkerThreads | null;\n workerFile: string | null;\n} {\n const wt = loadWorkerThreads();\n const os = loadOs();\n const workerFile = getWorkerPath('lockWorker.js');\n const numCpus = os ? Math.max(1, os.cpus().length) : 1;\n const numWorkers = Math.min(\n MAX_WORKERS,\n Math.max(1, Math.floor(itemCount / minPerWorker)),\n numCpus,\n );\n return { numWorkers, wt, workerFile };\n}\n\nasync function runWorkerChunks(\n wt: WorkerThreads,\n workerFile: string,\n chunks: WorkerInput[],\n): Promise<boolean> {\n const results = await mapWithConcurrency(\n chunks,\n maxConcurrentWorkers(chunks.length),\n async data => await runWorker(wt, workerFile, data),\n );\n return results.every(Boolean);\n}\n\nexport async function verifySharesBinding(\n shares: string[][],\n distributedKeys: string[],\n threshold: number,\n): Promise<boolean> {\n if (shares.length !== distributedKeys.length) return false;\n if (shares.length === 0) return true;\n\n const { numWorkers, wt, workerFile } = poolSize(\n shares.length,\n MIN_VALIDATORS_PER_WORKER,\n );\n\n // Inline the guard so TS narrows wt/workerFile for the parallel path below\n // (avoids non-null assertions).\n if (\n wt === null ||\n workerFile === null ||\n shares.length < MIN_PARALLEL_VALIDATORS ||\n numWorkers < 2\n ) {\n return verifySharesSync(shares, distributedKeys, threshold);\n }\n\n const shareChunks = chunkArrays(shares, numWorkers);\n const keyChunks = chunkArrays(distributedKeys, numWorkers);\n\n return await runWorkerChunks(\n wt,\n workerFile,\n shareChunks.map((chunk, i) => ({\n mode: 'shareBinding',\n shares: chunk,\n distributedKeys: keyChunks[i],\n threshold,\n })),\n );\n}\n\n/** Verify deposit + builder BLS signatures collected from `depositBlsCheck` / `builderBlsCheck`. */\nexport async function verifyBlsChecksParallel(\n checks: BlsSignatureCheck[],\n): Promise<boolean> {\n if (checks.length === 0) return true;\n return await verifyBatchParallel(\n checks.map(c => c.pubkey),\n checks.map(c => c.message),\n checks.map(c => c.signature),\n );\n}\n\n// Verify a batch of (pubkey, message, individual signature) triples.\n// Sync path aggregates all sigs and runs blsVerifyMultiple once (matches the\n// previous behavior). Parallel path splits into chunks; each chunk's sigs are\n// aggregated and verified independently — same total pairing count, K× wall\n// time speedup.\nexport async function verifyBatchParallel(\n pubkeys: Uint8Array[],\n messages: Uint8Array[],\n signatures: Uint8Array[],\n): Promise<boolean> {\n if (\n pubkeys.length !== messages.length ||\n pubkeys.length !== signatures.length\n ) {\n return false;\n }\n if (pubkeys.length === 0) return true;\n\n const { numWorkers, wt, workerFile } = poolSize(\n pubkeys.length,\n MIN_PAIRS_PER_WORKER,\n );\n\n if (\n wt === null ||\n workerFile === null ||\n pubkeys.length < MIN_PARALLEL_BATCH_PAIRS ||\n numWorkers < 2\n ) {\n return blsVerifyMultiple(\n pubkeys,\n messages,\n blsAggregateSignatures(signatures),\n );\n }\n\n const pkChunks = chunkArrays(pubkeys, numWorkers);\n const msgChunks = chunkArrays(messages, numWorkers);\n const sigChunks = chunkArrays(signatures, numWorkers);\n\n return await runWorkerChunks(\n wt,\n workerFile,\n pkChunks.map((pks, i) => ({\n mode: 'verifyBatch',\n pubkeys: pks,\n messages: msgChunks[i],\n signatures: sigChunks[i],\n })),\n );\n}\n\n// Lock signature_aggregate: aggregate many operator shares (validators × operators)\n// then verify one BLS signature over lock_hash. Parallel path aggregates pubkey\n// chunks in workers, combines partial aggregates on the main thread, then verifies.\nexport async function verifyAggregateParallel(\n pubkeys: Uint8Array[],\n message: Uint8Array,\n signature: Uint8Array,\n): Promise<boolean> {\n if (pubkeys.length === 0) return false;\n\n const { numWorkers, wt, workerFile } = poolSize(\n pubkeys.length,\n MIN_KEYS_PER_WORKER_AGG,\n );\n\n if (\n wt === null ||\n workerFile === null ||\n pubkeys.length < MIN_PARALLEL_AGGREGATE_KEYS ||\n numWorkers < 2\n ) {\n return blsVerifyAggregate(pubkeys, message, signature);\n }\n\n const pkChunks = chunkArrays(pubkeys, numWorkers);\n const partials = await mapWithConcurrency(\n pkChunks,\n maxConcurrentWorkers(pkChunks.length),\n async chunk =>\n await runWorkerAggregatePubkeys(wt, workerFile, {\n mode: 'aggregatePubkeys',\n pubkeys: chunk,\n }),\n );\n if (partials.some(p => p === null)) return false;\n\n const aggregated = blsAggregatePublicKeys(partials as Uint8Array[]);\n return blsVerifyWithAggregatedPubkey(aggregated, message, signature);\n}\n\n/**\n * Large locks: run full validation off the main thread (obol-api stays responsive).\n * Returns null when workers are unavailable — caller should use sync validation.\n */\nexport async function validateClusterLockInWorker(\n lock: ClusterLock,\n safeRpcUrl?: SafeRpcUrl,\n): Promise<boolean | null> {\n const n = lock.distributed_validators?.length ?? 0;\n if (n < MIN_VALIDATORS_FOR_VALIDATION_WORKER) return null;\n\n const wt = loadWorkerThreads();\n const workerFile = getWorkerPath('clusterLockValidationWorker.js');\n if (wt === null || workerFile === null) return null;\n\n return await new Promise((resolve, reject) => {\n let settled = false;\n const worker = new wt.Worker(workerFile, {\n workerData: { lock, safeRpcUrl },\n });\n let timer: ReturnType<typeof setTimeout>;\n const finish = (result: boolean | null): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n resolve(result);\n };\n // Timeout rejects (ClusterLockValidationTimeoutError) instead of resolving\n // false: callers must distinguish **504** gateway timeout from **400** invalid\n // crypto. Do not resolve null here — full sync fallback would block Node again.\n timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n void worker.terminate();\n reject(\n new ClusterLockValidationTimeoutError(VALIDATION_WORKER_TIMEOUT_MS),\n );\n }, VALIDATION_WORKER_TIMEOUT_MS);\n worker.once('message', (msg: unknown) => {\n finish(msg === true);\n });\n worker.once('error', () => {\n finish(null);\n });\n worker.once('exit', code => {\n if (code !== 0) finish(null);\n });\n });\n}\n","/**\n * Thrown when attempting to create a resource that already exists\n * (e.g. posting a duplicate cluster definition, or accepting already-accepted terms).\n */\nexport class ConflictError extends Error {\n name = 'ConflictError';\n\n constructor() {\n super('This Cluster has been already posted.');\n Object.setPrototypeOf(this, ConflictError.prototype);\n }\n}\n\n/**\n * Thrown when a method that requires an ethers `Signer` is called on a\n * client that was constructed without one.\n *\n * To fix: pass a `Wallet` or `JsonRpcSigner` as the second argument to\n * `new Client(config, signer)`.\n */\nexport class SignerRequiredError extends Error {\n name = 'SignerRequiredError';\n\n constructor(method: string) {\n super(`Signer is required in ${method}`);\n Object.setPrototypeOf(this, SignerRequiredError.prototype);\n }\n}\n\n/**\n * Thrown when an operation is attempted on a chain ID that does not support it\n * (e.g. deploying splitters on a chain without factory contracts).\n */\nexport class UnsupportedChainError extends Error {\n name = 'UnsupportedChainError';\n\n constructor(chainId: number, operation: string) {\n super(`${operation} is not supported on chain ${chainId}`);\n Object.setPrototypeOf(this, UnsupportedChainError.prototype);\n }\n}\n\n/**\n * Thrown when full lock validation (`validateClusterLock` worker path) exceeds\n * the worker time limit (large clusters). HTTP gateways should respond with\n * **504**; this is distinct from cryptographic failure (**false** → **400**).\n */\nexport class ClusterLockValidationTimeoutError extends Error {\n name = 'ClusterLockValidationTimeoutError';\n\n /**\n * @param timeoutMs - Same value as SDK whole-lock validation worker timeout.\n */\n constructor(public readonly timeoutMs: number) {\n super(\n `Cluster lock validation exceeded worker time limit (${timeoutMs} ms). Retry later; this does not imply invalid lock data.`,\n );\n Object.setPrototypeOf(this, ClusterLockValidationTimeoutError.prototype);\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/verification/parallelPool.ts","../../../src/errors.ts"],"sourcesContent":["// Parallel BLS verification via Node worker_threads.\n//\n// IMPORTANT: only the CJS build actually runs in parallel. ESM Node and\n// browser bundles transparently fall back to sync. See the matrix in\n// PARALLEL_VALIDATION_PLAN.md. The main consumer flow (charon DKG ->\n// obol-api NestJS handler -> SDK CJS) is CJS, so it gets the speedup.\n//\n// Strategy:\n// - Look up the worker file via __dirname (CJS only). If undefined or the\n// file isn't where we expect (ESM, browser, source-mode tsx/jest), return\n// undefined and fall back to sync.\n// - Below MIN_PARALLEL_* thresholds, sync wins (worker spin-up dominates).\n// - Spawn one worker per chunk, await all, collapse to a single boolean.\n\nimport { fromHexString } from '@chainsafe/ssz';\nimport {\n blsAggregatePublicKeys,\n blsAggregateSignatures,\n blsRecoverDistributedPubkeyFromShares,\n blsVerifyAggregate,\n blsVerifyExtraShares,\n blsVerifyMultiple,\n blsVerifyWithAggregatedPubkey,\n} from '../blsUtils.js';\n// Type-only imports — erased at compile time, so they don't pull node:*\n// modules into the browser bundle.\nimport type * as WTNs from 'node:worker_threads';\nimport type * as OsNs from 'node:os';\nimport type * as PathNs from 'node:path';\nimport type * as FsNs from 'node:fs';\nimport { ClusterLockValidationTimeoutError } from '../errors.js';\nimport type { BlsSignatureCheck, ClusterLock, SafeRpcUrl } from '../types.js';\nimport type { AggregatePubkeysInput, WorkerInput } from './lockWorker.js';\n\nconst MIN_PARALLEL_VALIDATORS = 50;\nconst MIN_PARALLEL_BATCH_PAIRS = 100;\nconst MAX_WORKERS = 8;\nconst MIN_VALIDATORS_PER_WORKER = 25;\nconst MIN_PAIRS_PER_WORKER = 50;\nconst MIN_PARALLEL_AGGREGATE_KEYS = 400;\nconst MIN_KEYS_PER_WORKER_AGG = 100;\n// Hard ceiling so a stuck worker can't leak past obol-api's HTTP timeout.\n// Biggest legitimate batch (~1000 pairs) finishes in ~3s on the bench; 60s\n// is conservative enough to never false-trip on real input.\nconst MIN_VALIDATORS_FOR_VALIDATION_WORKER = 50;\nconst VALIDATION_WORKER_TIMEOUT_MS = 120_000;\nconst WORKER_TIMEOUT_MS = 60_000;\n// Cap simultaneous workers (memory). Scales up for large jobs on multi-core hosts.\nconst MAX_CONCURRENT_WORKERS_CAP = 8;\n\nfunction maxConcurrentWorkers(numWorkers: number): number {\n return Math.min(numWorkers, MAX_CONCURRENT_WORKERS_CAP);\n}\n\ntype WorkerThreads = typeof WTNs;\ntype NodeOs = typeof OsNs;\n\nlet workerThreadsCache: WorkerThreads | null | undefined;\nlet osCache: NodeOs | null | undefined;\n// Tri-state per worker file: undefined = uncached, null = unavailable, string = path.\nconst workerPathByFile = new Map<string, string | null | undefined>();\n\nfunction loadWorkerThreads(): WorkerThreads | null {\n if (workerThreadsCache !== undefined) return workerThreadsCache;\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires\n workerThreadsCache = require('node:worker_threads') as WorkerThreads;\n } catch {\n workerThreadsCache = null;\n }\n return workerThreadsCache;\n}\n\nfunction loadOs(): NodeOs | null {\n if (osCache !== undefined) return osCache;\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires\n osCache = require('node:os') as NodeOs;\n } catch {\n osCache = null;\n }\n return osCache;\n}\n\nfunction getWorkerPath(filename: string): string | null {\n const cached = workerPathByFile.get(filename);\n if (cached !== undefined) return cached;\n if (typeof __dirname === 'undefined') {\n workerPathByFile.set(filename, null);\n return null;\n }\n // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires\n const path = require('node:path') as typeof PathNs;\n // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires\n const fs = require('node:fs') as typeof FsNs;\n // Bundled index.js: __dirname is dist/cjs/src; workers live in verification/.\n // Standalone parallelPool.js entry: __dirname is already verification/.\n const candidates = [\n path.join(__dirname, filename),\n path.join(__dirname, 'verification', filename),\n ];\n try {\n for (const candidate of candidates) {\n if (fs.existsSync(candidate)) {\n workerPathByFile.set(filename, candidate);\n return candidate;\n }\n }\n } catch {\n // fall through\n }\n workerPathByFile.set(filename, null);\n return null;\n}\n\nasync function mapWithConcurrency<T, R>(\n items: T[],\n concurrency: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results: R[] = new Array(items.length);\n let next = 0;\n const runners = Array.from(\n { length: Math.min(concurrency, items.length) },\n async () => {\n while (next < items.length) {\n const i = next++;\n results[i] = await fn(items[i], i);\n }\n },\n );\n await Promise.all(runners);\n return results;\n}\n\nfunction chunkArrays<T>(arr: T[], n: number): T[][] {\n const size = Math.ceil(arr.length / n);\n const out: T[][] = [];\n for (let i = 0; i < arr.length; i += size) {\n out.push(arr.slice(i, i + size));\n }\n return out;\n}\n\nfunction verifySharesSync(\n shares: string[][],\n distributedKeys: string[],\n threshold: number,\n): boolean {\n for (let i = 0; i < shares.length; i++) {\n const sharesBytes = shares[i].map(s => fromHexString(s));\n const dkBytes = fromHexString(distributedKeys[i]);\n const recovered = blsRecoverDistributedPubkeyFromShares(\n sharesBytes,\n threshold,\n );\n if (!recovered) return false;\n if (recovered.length !== dkBytes.length) return false;\n for (let j = 0; j < recovered.length; j++) {\n if (recovered[j] !== dkBytes[j]) return false;\n }\n if (!blsVerifyExtraShares(sharesBytes, threshold, dkBytes)) {\n return false;\n }\n }\n return true;\n}\n\nasync function runWorkerAggregatePubkeys(\n wt: WorkerThreads,\n workerFile: string,\n data: AggregatePubkeysInput,\n): Promise<Uint8Array | null> {\n return await new Promise(resolve => {\n let settled = false;\n const worker = new wt.Worker(workerFile, { workerData: data });\n const finish = (result: Uint8Array | null): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n resolve(result);\n };\n const timer = setTimeout(() => {\n finish(null);\n }, WORKER_TIMEOUT_MS);\n worker.once('message', (msg: unknown) => {\n finish(msg instanceof Uint8Array ? msg : null);\n });\n worker.once('error', () => {\n finish(null);\n });\n worker.once('exit', code => {\n if (code !== 0) finish(null);\n });\n });\n}\n\nasync function runWorker(\n wt: WorkerThreads,\n workerFile: string,\n data: WorkerInput,\n): Promise<boolean> {\n return await new Promise(resolve => {\n let settled = false;\n const worker = new wt.Worker(workerFile, { workerData: data });\n const finish = (result: boolean): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n resolve(result);\n };\n const timer = setTimeout(() => {\n finish(false);\n }, WORKER_TIMEOUT_MS);\n worker.once('message', (msg: unknown) => {\n finish(msg === true);\n });\n worker.once('error', () => {\n finish(false);\n });\n worker.once('exit', code => {\n if (code !== 0) finish(false);\n });\n });\n}\n\nfunction poolSize(\n itemCount: number,\n minPerWorker: number,\n): {\n numWorkers: number;\n wt: WorkerThreads | null;\n workerFile: string | null;\n} {\n const wt = loadWorkerThreads();\n const os = loadOs();\n const workerFile = getWorkerPath('lockWorker.js');\n const numCpus = os ? Math.max(1, os.cpus().length) : 1;\n const numWorkers = Math.min(\n MAX_WORKERS,\n Math.max(1, Math.floor(itemCount / minPerWorker)),\n numCpus,\n );\n return { numWorkers, wt, workerFile };\n}\n\nasync function runWorkerChunks(\n wt: WorkerThreads,\n workerFile: string,\n chunks: WorkerInput[],\n): Promise<boolean> {\n const results = await mapWithConcurrency(\n chunks,\n maxConcurrentWorkers(chunks.length),\n async data => await runWorker(wt, workerFile, data),\n );\n return results.every(Boolean);\n}\n\nexport async function verifySharesBinding(\n shares: string[][],\n distributedKeys: string[],\n threshold: number,\n): Promise<boolean> {\n if (shares.length !== distributedKeys.length) return false;\n if (shares.length === 0) return true;\n\n const { numWorkers, wt, workerFile } = poolSize(\n shares.length,\n MIN_VALIDATORS_PER_WORKER,\n );\n\n // Inline the guard so TS narrows wt/workerFile for the parallel path below\n // (avoids non-null assertions).\n if (\n wt === null ||\n workerFile === null ||\n shares.length < MIN_PARALLEL_VALIDATORS ||\n numWorkers < 2\n ) {\n return verifySharesSync(shares, distributedKeys, threshold);\n }\n\n const shareChunks = chunkArrays(shares, numWorkers);\n const keyChunks = chunkArrays(distributedKeys, numWorkers);\n\n return await runWorkerChunks(\n wt,\n workerFile,\n shareChunks.map((chunk, i) => ({\n mode: 'shareBinding',\n shares: chunk,\n distributedKeys: keyChunks[i],\n threshold,\n })),\n );\n}\n\n/** Verify deposit + builder BLS signatures collected from `depositBlsCheck` / `builderBlsCheck`. */\nexport async function verifyBlsChecksParallel(\n checks: BlsSignatureCheck[],\n): Promise<boolean> {\n if (checks.length === 0) return true;\n return await verifyBatchParallel(\n checks.map(c => c.pubkey),\n checks.map(c => c.message),\n checks.map(c => c.signature),\n );\n}\n\n// Verify a batch of (pubkey, message, individual signature) triples.\n// Sync path aggregates all sigs and runs blsVerifyMultiple once (matches the\n// previous behavior). Parallel path splits into chunks; each chunk's sigs are\n// aggregated and verified independently — same total pairing count, K× wall\n// time speedup.\nexport async function verifyBatchParallel(\n pubkeys: Uint8Array[],\n messages: Uint8Array[],\n signatures: Uint8Array[],\n): Promise<boolean> {\n if (\n pubkeys.length !== messages.length ||\n pubkeys.length !== signatures.length\n ) {\n return false;\n }\n if (pubkeys.length === 0) return true;\n\n const { numWorkers, wt, workerFile } = poolSize(\n pubkeys.length,\n MIN_PAIRS_PER_WORKER,\n );\n\n if (\n wt === null ||\n workerFile === null ||\n pubkeys.length < MIN_PARALLEL_BATCH_PAIRS ||\n numWorkers < 2\n ) {\n return blsVerifyMultiple(\n pubkeys,\n messages,\n blsAggregateSignatures(signatures),\n );\n }\n\n const pkChunks = chunkArrays(pubkeys, numWorkers);\n const msgChunks = chunkArrays(messages, numWorkers);\n const sigChunks = chunkArrays(signatures, numWorkers);\n\n return await runWorkerChunks(\n wt,\n workerFile,\n pkChunks.map((pks, i) => ({\n mode: 'verifyBatch',\n pubkeys: pks,\n messages: msgChunks[i],\n signatures: sigChunks[i],\n })),\n );\n}\n\n// Lock signature_aggregate: aggregate many operator shares (validators × operators)\n// then verify one BLS signature over lock_hash. Parallel path aggregates pubkey\n// chunks in workers, combines partial aggregates on the main thread, then verifies.\nexport async function verifyAggregateParallel(\n pubkeys: Uint8Array[],\n message: Uint8Array,\n signature: Uint8Array,\n): Promise<boolean> {\n if (pubkeys.length === 0) return false;\n\n const { numWorkers, wt, workerFile } = poolSize(\n pubkeys.length,\n MIN_KEYS_PER_WORKER_AGG,\n );\n\n if (\n wt === null ||\n workerFile === null ||\n pubkeys.length < MIN_PARALLEL_AGGREGATE_KEYS ||\n numWorkers < 2\n ) {\n return blsVerifyAggregate(pubkeys, message, signature);\n }\n\n const pkChunks = chunkArrays(pubkeys, numWorkers);\n const partials = await mapWithConcurrency(\n pkChunks,\n maxConcurrentWorkers(pkChunks.length),\n async chunk =>\n await runWorkerAggregatePubkeys(wt, workerFile, {\n mode: 'aggregatePubkeys',\n pubkeys: chunk,\n }),\n );\n if (partials.some(p => p === null)) return false;\n\n const aggregated = blsAggregatePublicKeys(partials as Uint8Array[]);\n return blsVerifyWithAggregatedPubkey(aggregated, message, signature);\n}\n\n/**\n * Large locks: run full validation off the main thread (obol-api stays responsive).\n * Returns null when workers are unavailable — caller should use sync validation.\n */\nexport async function validateClusterLockInWorker(\n lock: ClusterLock,\n safeRpcUrl?: SafeRpcUrl,\n): Promise<boolean | null> {\n const n = lock.distributed_validators?.length ?? 0;\n if (n < MIN_VALIDATORS_FOR_VALIDATION_WORKER) return null;\n\n const wt = loadWorkerThreads();\n const workerFile = getWorkerPath('clusterLockValidationWorker.js');\n if (wt === null || workerFile === null) return null;\n\n return await new Promise((resolve, reject) => {\n let settled = false;\n const worker = new wt.Worker(workerFile, {\n workerData: { lock, safeRpcUrl },\n });\n // Timeout rejects (ClusterLockValidationTimeoutError) instead of resolving\n // false: callers must distinguish **504** gateway timeout from **400** invalid\n // crypto. Do not resolve null here — full sync fallback would block Node again.\n const timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n void worker.terminate();\n reject(\n new ClusterLockValidationTimeoutError(VALIDATION_WORKER_TIMEOUT_MS),\n );\n }, VALIDATION_WORKER_TIMEOUT_MS);\n const finish = (result: boolean | null): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n resolve(result);\n };\n worker.once('message', (msg: unknown) => {\n finish(msg === true);\n });\n worker.once('error', () => {\n finish(null);\n });\n worker.once('exit', code => {\n if (code !== 0) finish(null);\n });\n });\n}\n","/**\n * Thrown when attempting to create a resource that already exists\n * (e.g. posting a duplicate cluster definition, or accepting already-accepted terms).\n */\nexport class ConflictError extends Error {\n name = 'ConflictError';\n\n constructor() {\n super('This Cluster has been already posted.');\n Object.setPrototypeOf(this, ConflictError.prototype);\n }\n}\n\n/**\n * Thrown when a method that requires an ethers `Signer` is called on a\n * client that was constructed without one.\n *\n * To fix: pass a `Wallet` or `JsonRpcSigner` as the second argument to\n * `new Client(config, signer)`.\n */\nexport class SignerRequiredError extends Error {\n name = 'SignerRequiredError';\n\n constructor(method: string) {\n super(`Signer is required in ${method}`);\n Object.setPrototypeOf(this, SignerRequiredError.prototype);\n }\n}\n\n/**\n * Thrown when an operation is attempted on a chain ID that does not support it\n * (e.g. deploying splitters on a chain without factory contracts).\n */\nexport class UnsupportedChainError extends Error {\n name = 'UnsupportedChainError';\n\n constructor(chainId: number, operation: string) {\n super(`${operation} is not supported on chain ${chainId}`);\n Object.setPrototypeOf(this, UnsupportedChainError.prototype);\n }\n}\n\n/**\n * Thrown when full lock validation (`validateClusterLock` worker path) exceeds\n * the worker time limit (large clusters). HTTP gateways should respond with\n * **504**; this is distinct from cryptographic failure (**false** → **400**).\n */\n/**\n * Thrown when {@link Client} is constructed with a baseUrl that is not an\n * an allowed Obol API base URL (see {@link ALLOWED_OBOL_API_BASE_URLS}).\n */\nexport class InvalidBaseUrlError extends Error {\n name = 'InvalidBaseUrlError';\n\n constructor(message: string) {\n super(message);\n Object.setPrototypeOf(this, InvalidBaseUrlError.prototype);\n }\n}\n\nexport class ClusterLockValidationTimeoutError extends Error {\n name = 'ClusterLockValidationTimeoutError';\n\n /**\n * @param timeoutMs - Same value as SDK whole-lock validation worker timeout.\n */\n constructor(public readonly timeoutMs: number) {\n super(\n `Cluster lock validation exceeded worker time limit (${timeoutMs} ms). Retry later; this does not imply invalid lock data.`,\n );\n Object.setPrototypeOf(this, ClusterLockValidationTimeoutError.prototype);\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/esm/src/index.js
CHANGED
|
@@ -57,14 +57,15 @@ import {
|
|
|
57
57
|
verifyBuilderRegistration,
|
|
58
58
|
verifyDepositData,
|
|
59
59
|
verifyNodeSignatures
|
|
60
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-T74RX3N2.js";
|
|
61
61
|
import {
|
|
62
62
|
ClusterLockValidationTimeoutError,
|
|
63
63
|
ConflictError,
|
|
64
|
+
InvalidBaseUrlError,
|
|
64
65
|
SignerRequiredError,
|
|
65
66
|
UnsupportedChainError,
|
|
66
67
|
validateClusterLockInWorker
|
|
67
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-YSXFCSVK.js";
|
|
68
69
|
import {
|
|
69
70
|
__async,
|
|
70
71
|
__spreadProps,
|
|
@@ -77,12 +78,30 @@ import {
|
|
|
77
78
|
import { v4 as uuidv4 } from "uuid";
|
|
78
79
|
|
|
79
80
|
// src/base.ts
|
|
81
|
+
var ALLOWED_OBOL_API_BASE_URLS = [
|
|
82
|
+
DEFAULT_BASE_URL,
|
|
83
|
+
"https://obol-api-nonprod-dev.dev.obol.tech",
|
|
84
|
+
"https://obol-api-nonprod-qa.dev.obol.tech"
|
|
85
|
+
];
|
|
86
|
+
var ALLOWED_BASE_URLS = new Set(ALLOWED_OBOL_API_BASE_URLS);
|
|
87
|
+
function assertAllowedBaseUrl(baseUrl) {
|
|
88
|
+
let candidate = baseUrl.trim();
|
|
89
|
+
while (candidate.endsWith("/")) {
|
|
90
|
+
candidate = candidate.slice(0, -1);
|
|
91
|
+
}
|
|
92
|
+
if (!ALLOWED_BASE_URLS.has(candidate)) {
|
|
93
|
+
throw new InvalidBaseUrlError(
|
|
94
|
+
`baseUrl must be one of: ${ALLOWED_OBOL_API_BASE_URLS.join(", ")}`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
return candidate;
|
|
98
|
+
}
|
|
80
99
|
var Base = class {
|
|
81
100
|
constructor({
|
|
82
101
|
baseUrl = DEFAULT_BASE_URL,
|
|
83
102
|
chainId = DEFAULT_CHAIN_ID
|
|
84
103
|
}) {
|
|
85
|
-
this.baseUrl = baseUrl;
|
|
104
|
+
this.baseUrl = assertAllowedBaseUrl(baseUrl);
|
|
86
105
|
this.chainId = chainId;
|
|
87
106
|
this.fork_version = FORK_MAPPING[this.chainId];
|
|
88
107
|
}
|
|
@@ -5064,6 +5083,7 @@ var Client = class extends Base {
|
|
|
5064
5083
|
*
|
|
5065
5084
|
* @param config - Client configuration object.
|
|
5066
5085
|
* @param config.baseUrl - Obol API base URL. Defaults to `https://api.obol.tech`.
|
|
5086
|
+
* Must match one of {@link ALLOWED_OBOL_API_BASE_URLS} (API paths use `/v1/...` separately).
|
|
5067
5087
|
* @param config.chainId - Target chain ID. Defaults to `560048` (Hoodi).
|
|
5068
5088
|
* Supported: 1 (Mainnet), 560048 (Hoodi), 100 (Gnosis), 11155111 (Sepolia).
|
|
5069
5089
|
* @param signer - An ethers `Wallet` or `JsonRpcSigner`. Required for any
|
|
@@ -5644,6 +5664,7 @@ var Client = class extends Base {
|
|
|
5644
5664
|
}
|
|
5645
5665
|
};
|
|
5646
5666
|
export {
|
|
5667
|
+
ALLOWED_OBOL_API_BASE_URLS,
|
|
5647
5668
|
AVAILABLE_SPLITTER_CHAINS,
|
|
5648
5669
|
CAPELLA_FORK_MAPPING,
|
|
5649
5670
|
CHAIN_CONFIGURATION,
|
|
@@ -5676,6 +5697,7 @@ export {
|
|
|
5676
5697
|
FORK_NAMES,
|
|
5677
5698
|
GENESIS_VALIDATOR_ROOT,
|
|
5678
5699
|
Incentives,
|
|
5700
|
+
InvalidBaseUrlError,
|
|
5679
5701
|
OBOL_SDK_EMAIL,
|
|
5680
5702
|
ObolSplits,
|
|
5681
5703
|
OperatorConfigHashSigningTypes,
|