@obolnetwork/obol-sdk 2.11.13 → 2.12.1
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/dist/browser/src/index.js +702 -212
- package/dist/browser/src/index.js.map +1 -1
- package/dist/cjs/src/blsUtils.js +3843 -0
- package/dist/cjs/src/blsUtils.js.map +1 -0
- package/dist/cjs/src/index.js +790 -299
- package/dist/cjs/src/index.js.map +1 -1
- package/dist/cjs/src/verification/clusterLockValidationWorker.js +14696 -0
- package/dist/cjs/src/verification/clusterLockValidationWorker.js.map +1 -0
- package/dist/cjs/src/verification/lockWorker.js +3840 -0
- package/dist/cjs/src/verification/lockWorker.js.map +1 -0
- package/dist/cjs/src/verification/parallelPool.js +4185 -0
- package/dist/cjs/src/verification/parallelPool.js.map +1 -0
- package/dist/esm/src/blsUtils.js +21 -0
- package/dist/esm/src/blsUtils.js.map +1 -0
- package/dist/esm/src/chunk-5ASVONSJ.js +10242 -0
- package/dist/esm/src/chunk-5ASVONSJ.js.map +1 -0
- package/dist/esm/src/chunk-LOSYOJC3.js +4370 -0
- package/dist/esm/src/chunk-LOSYOJC3.js.map +1 -0
- package/dist/esm/src/chunk-RYTIXFRX.js +382 -0
- package/dist/esm/src/chunk-RYTIXFRX.js.map +1 -0
- package/dist/esm/src/index.js +215 -14508
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/verification/clusterLockValidationWorker.js +18 -0
- package/dist/esm/src/verification/clusterLockValidationWorker.js.map +1 -0
- package/dist/esm/src/verification/lockWorker.js +55 -0
- package/dist/esm/src/verification/lockWorker.js.map +1 -0
- package/dist/esm/src/verification/parallelPool.js +16 -0
- package/dist/esm/src/verification/parallelPool.js.map +1 -0
- package/dist/types/src/blsUtils.d.ts +5 -0
- package/dist/types/src/errors.d.ts +13 -0
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/services.d.ts +4 -1
- package/dist/types/src/types.d.ts +6 -0
- package/dist/types/src/verification/clusterLockValidationWorker.d.ts +1 -0
- package/dist/types/src/verification/common.d.ts +26 -7
- package/dist/types/src/verification/lockWorker.d.ts +18 -0
- package/dist/types/src/verification/parallelPool.d.ts +11 -0
- package/dist/types/test/verification/parallelPool.spec.d.ts +1 -0
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__async,
|
|
3
|
+
__require,
|
|
4
|
+
blsAggregatePublicKeys,
|
|
5
|
+
blsAggregateSignatures,
|
|
6
|
+
blsRecoverDistributedPubkeyFromShares,
|
|
7
|
+
blsVerifyAggregate,
|
|
8
|
+
blsVerifyExtraShares,
|
|
9
|
+
blsVerifyMultiple,
|
|
10
|
+
blsVerifyWithAggregatedPubkey
|
|
11
|
+
} from "./chunk-LOSYOJC3.js";
|
|
12
|
+
|
|
13
|
+
// src/verification/parallelPool.ts
|
|
14
|
+
import { fromHexString } from "@chainsafe/ssz";
|
|
15
|
+
|
|
16
|
+
// src/errors.ts
|
|
17
|
+
var ConflictError = class _ConflictError extends Error {
|
|
18
|
+
constructor() {
|
|
19
|
+
super("This Cluster has been already posted.");
|
|
20
|
+
this.name = "ConflictError";
|
|
21
|
+
Object.setPrototypeOf(this, _ConflictError.prototype);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
var SignerRequiredError = class _SignerRequiredError extends Error {
|
|
25
|
+
constructor(method) {
|
|
26
|
+
super(`Signer is required in ${method}`);
|
|
27
|
+
this.name = "SignerRequiredError";
|
|
28
|
+
Object.setPrototypeOf(this, _SignerRequiredError.prototype);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
var UnsupportedChainError = class _UnsupportedChainError extends Error {
|
|
32
|
+
constructor(chainId, operation) {
|
|
33
|
+
super(`${operation} is not supported on chain ${chainId}`);
|
|
34
|
+
this.name = "UnsupportedChainError";
|
|
35
|
+
Object.setPrototypeOf(this, _UnsupportedChainError.prototype);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
var ClusterLockValidationTimeoutError = class _ClusterLockValidationTimeoutError extends Error {
|
|
39
|
+
/**
|
|
40
|
+
* @param timeoutMs - Same value as SDK whole-lock validation worker timeout.
|
|
41
|
+
*/
|
|
42
|
+
constructor(timeoutMs) {
|
|
43
|
+
super(
|
|
44
|
+
`Cluster lock validation exceeded worker time limit (${timeoutMs} ms). Retry later; this does not imply invalid lock data.`
|
|
45
|
+
);
|
|
46
|
+
this.timeoutMs = timeoutMs;
|
|
47
|
+
this.name = "ClusterLockValidationTimeoutError";
|
|
48
|
+
Object.setPrototypeOf(this, _ClusterLockValidationTimeoutError.prototype);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// src/verification/parallelPool.ts
|
|
53
|
+
var MIN_PARALLEL_VALIDATORS = 50;
|
|
54
|
+
var MIN_PARALLEL_BATCH_PAIRS = 100;
|
|
55
|
+
var MAX_WORKERS = 8;
|
|
56
|
+
var MIN_VALIDATORS_PER_WORKER = 25;
|
|
57
|
+
var MIN_PAIRS_PER_WORKER = 50;
|
|
58
|
+
var MIN_PARALLEL_AGGREGATE_KEYS = 400;
|
|
59
|
+
var MIN_KEYS_PER_WORKER_AGG = 100;
|
|
60
|
+
var MIN_VALIDATORS_FOR_VALIDATION_WORKER = 50;
|
|
61
|
+
var VALIDATION_WORKER_TIMEOUT_MS = 12e4;
|
|
62
|
+
var WORKER_TIMEOUT_MS = 6e4;
|
|
63
|
+
var MAX_CONCURRENT_WORKERS_CAP = 8;
|
|
64
|
+
function maxConcurrentWorkers(numWorkers) {
|
|
65
|
+
return Math.min(numWorkers, MAX_CONCURRENT_WORKERS_CAP);
|
|
66
|
+
}
|
|
67
|
+
var workerThreadsCache;
|
|
68
|
+
var osCache;
|
|
69
|
+
var workerPathByFile = /* @__PURE__ */ new Map();
|
|
70
|
+
function loadWorkerThreads() {
|
|
71
|
+
if (workerThreadsCache !== void 0) return workerThreadsCache;
|
|
72
|
+
try {
|
|
73
|
+
workerThreadsCache = __require("worker_threads");
|
|
74
|
+
} catch (e) {
|
|
75
|
+
workerThreadsCache = null;
|
|
76
|
+
}
|
|
77
|
+
return workerThreadsCache;
|
|
78
|
+
}
|
|
79
|
+
function loadOs() {
|
|
80
|
+
if (osCache !== void 0) return osCache;
|
|
81
|
+
try {
|
|
82
|
+
osCache = __require("os");
|
|
83
|
+
} catch (e) {
|
|
84
|
+
osCache = null;
|
|
85
|
+
}
|
|
86
|
+
return osCache;
|
|
87
|
+
}
|
|
88
|
+
function getWorkerPath(filename) {
|
|
89
|
+
const cached = workerPathByFile.get(filename);
|
|
90
|
+
if (cached !== void 0) return cached;
|
|
91
|
+
if (typeof __dirname === "undefined") {
|
|
92
|
+
workerPathByFile.set(filename, null);
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const path = __require("path");
|
|
96
|
+
const fs = __require("fs");
|
|
97
|
+
const candidates = [
|
|
98
|
+
path.join(__dirname, filename),
|
|
99
|
+
path.join(__dirname, "verification", filename)
|
|
100
|
+
];
|
|
101
|
+
try {
|
|
102
|
+
for (const candidate of candidates) {
|
|
103
|
+
if (fs.existsSync(candidate)) {
|
|
104
|
+
workerPathByFile.set(filename, candidate);
|
|
105
|
+
return candidate;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
} catch (e) {
|
|
109
|
+
}
|
|
110
|
+
workerPathByFile.set(filename, null);
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
function mapWithConcurrency(items, concurrency, fn) {
|
|
114
|
+
return __async(this, null, function* () {
|
|
115
|
+
const results = new Array(items.length);
|
|
116
|
+
let next = 0;
|
|
117
|
+
const runners = Array.from(
|
|
118
|
+
{ length: Math.min(concurrency, items.length) },
|
|
119
|
+
() => __async(null, null, function* () {
|
|
120
|
+
while (next < items.length) {
|
|
121
|
+
const i = next++;
|
|
122
|
+
results[i] = yield fn(items[i], i);
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
);
|
|
126
|
+
yield Promise.all(runners);
|
|
127
|
+
return results;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
function chunkArrays(arr, n) {
|
|
131
|
+
const size = Math.ceil(arr.length / n);
|
|
132
|
+
const out = [];
|
|
133
|
+
for (let i = 0; i < arr.length; i += size) {
|
|
134
|
+
out.push(arr.slice(i, i + size));
|
|
135
|
+
}
|
|
136
|
+
return out;
|
|
137
|
+
}
|
|
138
|
+
function verifySharesSync(shares, distributedKeys, threshold) {
|
|
139
|
+
for (let i = 0; i < shares.length; i++) {
|
|
140
|
+
const sharesBytes = shares[i].map((s) => fromHexString(s));
|
|
141
|
+
const dkBytes = fromHexString(distributedKeys[i]);
|
|
142
|
+
const recovered = blsRecoverDistributedPubkeyFromShares(
|
|
143
|
+
sharesBytes,
|
|
144
|
+
threshold
|
|
145
|
+
);
|
|
146
|
+
if (!recovered) return false;
|
|
147
|
+
if (recovered.length !== dkBytes.length) return false;
|
|
148
|
+
for (let j = 0; j < recovered.length; j++) {
|
|
149
|
+
if (recovered[j] !== dkBytes[j]) return false;
|
|
150
|
+
}
|
|
151
|
+
if (!blsVerifyExtraShares(sharesBytes, threshold, dkBytes)) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
function runWorkerAggregatePubkeys(wt, workerFile, data) {
|
|
158
|
+
return __async(this, null, function* () {
|
|
159
|
+
return yield new Promise((resolve) => {
|
|
160
|
+
let settled = false;
|
|
161
|
+
const worker = new wt.Worker(workerFile, { workerData: data });
|
|
162
|
+
const finish = (result) => {
|
|
163
|
+
if (settled) return;
|
|
164
|
+
settled = true;
|
|
165
|
+
clearTimeout(timer);
|
|
166
|
+
worker.terminate();
|
|
167
|
+
resolve(result);
|
|
168
|
+
};
|
|
169
|
+
const timer = setTimeout(() => {
|
|
170
|
+
finish(null);
|
|
171
|
+
}, WORKER_TIMEOUT_MS);
|
|
172
|
+
worker.once("message", (msg) => {
|
|
173
|
+
finish(msg instanceof Uint8Array ? msg : null);
|
|
174
|
+
});
|
|
175
|
+
worker.once("error", () => {
|
|
176
|
+
finish(null);
|
|
177
|
+
});
|
|
178
|
+
worker.once("exit", (code) => {
|
|
179
|
+
if (code !== 0) finish(null);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
function runWorker(wt, workerFile, data) {
|
|
185
|
+
return __async(this, null, function* () {
|
|
186
|
+
return yield new Promise((resolve) => {
|
|
187
|
+
let settled = false;
|
|
188
|
+
const worker = new wt.Worker(workerFile, { workerData: data });
|
|
189
|
+
const finish = (result) => {
|
|
190
|
+
if (settled) return;
|
|
191
|
+
settled = true;
|
|
192
|
+
clearTimeout(timer);
|
|
193
|
+
worker.terminate();
|
|
194
|
+
resolve(result);
|
|
195
|
+
};
|
|
196
|
+
const timer = setTimeout(() => {
|
|
197
|
+
finish(false);
|
|
198
|
+
}, WORKER_TIMEOUT_MS);
|
|
199
|
+
worker.once("message", (msg) => {
|
|
200
|
+
finish(msg === true);
|
|
201
|
+
});
|
|
202
|
+
worker.once("error", () => {
|
|
203
|
+
finish(false);
|
|
204
|
+
});
|
|
205
|
+
worker.once("exit", (code) => {
|
|
206
|
+
if (code !== 0) finish(false);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
function poolSize(itemCount, minPerWorker) {
|
|
212
|
+
const wt = loadWorkerThreads();
|
|
213
|
+
const os = loadOs();
|
|
214
|
+
const workerFile = getWorkerPath("lockWorker.js");
|
|
215
|
+
const numCpus = os ? Math.max(1, os.cpus().length) : 1;
|
|
216
|
+
const numWorkers = Math.min(
|
|
217
|
+
MAX_WORKERS,
|
|
218
|
+
Math.max(1, Math.floor(itemCount / minPerWorker)),
|
|
219
|
+
numCpus
|
|
220
|
+
);
|
|
221
|
+
return { numWorkers, wt, workerFile };
|
|
222
|
+
}
|
|
223
|
+
function runWorkerChunks(wt, workerFile, chunks) {
|
|
224
|
+
return __async(this, null, function* () {
|
|
225
|
+
const results = yield mapWithConcurrency(
|
|
226
|
+
chunks,
|
|
227
|
+
maxConcurrentWorkers(chunks.length),
|
|
228
|
+
(data) => __async(null, null, function* () {
|
|
229
|
+
return yield runWorker(wt, workerFile, data);
|
|
230
|
+
})
|
|
231
|
+
);
|
|
232
|
+
return results.every(Boolean);
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
function verifySharesBinding(shares, distributedKeys, threshold) {
|
|
236
|
+
return __async(this, null, function* () {
|
|
237
|
+
if (shares.length !== distributedKeys.length) return false;
|
|
238
|
+
if (shares.length === 0) return true;
|
|
239
|
+
const { numWorkers, wt, workerFile } = poolSize(
|
|
240
|
+
shares.length,
|
|
241
|
+
MIN_VALIDATORS_PER_WORKER
|
|
242
|
+
);
|
|
243
|
+
if (wt === null || workerFile === null || shares.length < MIN_PARALLEL_VALIDATORS || numWorkers < 2) {
|
|
244
|
+
return verifySharesSync(shares, distributedKeys, threshold);
|
|
245
|
+
}
|
|
246
|
+
const shareChunks = chunkArrays(shares, numWorkers);
|
|
247
|
+
const keyChunks = chunkArrays(distributedKeys, numWorkers);
|
|
248
|
+
return yield runWorkerChunks(
|
|
249
|
+
wt,
|
|
250
|
+
workerFile,
|
|
251
|
+
shareChunks.map((chunk, i) => ({
|
|
252
|
+
mode: "shareBinding",
|
|
253
|
+
shares: chunk,
|
|
254
|
+
distributedKeys: keyChunks[i],
|
|
255
|
+
threshold
|
|
256
|
+
}))
|
|
257
|
+
);
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
function verifyBlsChecksParallel(checks) {
|
|
261
|
+
return __async(this, null, function* () {
|
|
262
|
+
if (checks.length === 0) return true;
|
|
263
|
+
return yield verifyBatchParallel(
|
|
264
|
+
checks.map((c) => c.pubkey),
|
|
265
|
+
checks.map((c) => c.message),
|
|
266
|
+
checks.map((c) => c.signature)
|
|
267
|
+
);
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
function verifyBatchParallel(pubkeys, messages, signatures) {
|
|
271
|
+
return __async(this, null, function* () {
|
|
272
|
+
if (pubkeys.length !== messages.length || pubkeys.length !== signatures.length) {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
if (pubkeys.length === 0) return true;
|
|
276
|
+
const { numWorkers, wt, workerFile } = poolSize(
|
|
277
|
+
pubkeys.length,
|
|
278
|
+
MIN_PAIRS_PER_WORKER
|
|
279
|
+
);
|
|
280
|
+
if (wt === null || workerFile === null || pubkeys.length < MIN_PARALLEL_BATCH_PAIRS || numWorkers < 2) {
|
|
281
|
+
return blsVerifyMultiple(
|
|
282
|
+
pubkeys,
|
|
283
|
+
messages,
|
|
284
|
+
blsAggregateSignatures(signatures)
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
const pkChunks = chunkArrays(pubkeys, numWorkers);
|
|
288
|
+
const msgChunks = chunkArrays(messages, numWorkers);
|
|
289
|
+
const sigChunks = chunkArrays(signatures, numWorkers);
|
|
290
|
+
return yield runWorkerChunks(
|
|
291
|
+
wt,
|
|
292
|
+
workerFile,
|
|
293
|
+
pkChunks.map((pks, i) => ({
|
|
294
|
+
mode: "verifyBatch",
|
|
295
|
+
pubkeys: pks,
|
|
296
|
+
messages: msgChunks[i],
|
|
297
|
+
signatures: sigChunks[i]
|
|
298
|
+
}))
|
|
299
|
+
);
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
function verifyAggregateParallel(pubkeys, message, signature) {
|
|
303
|
+
return __async(this, null, function* () {
|
|
304
|
+
if (pubkeys.length === 0) return false;
|
|
305
|
+
const { numWorkers, wt, workerFile } = poolSize(
|
|
306
|
+
pubkeys.length,
|
|
307
|
+
MIN_KEYS_PER_WORKER_AGG
|
|
308
|
+
);
|
|
309
|
+
if (wt === null || workerFile === null || pubkeys.length < MIN_PARALLEL_AGGREGATE_KEYS || numWorkers < 2) {
|
|
310
|
+
return blsVerifyAggregate(pubkeys, message, signature);
|
|
311
|
+
}
|
|
312
|
+
const pkChunks = chunkArrays(pubkeys, numWorkers);
|
|
313
|
+
const partials = yield mapWithConcurrency(
|
|
314
|
+
pkChunks,
|
|
315
|
+
maxConcurrentWorkers(pkChunks.length),
|
|
316
|
+
(chunk) => __async(null, null, function* () {
|
|
317
|
+
return yield runWorkerAggregatePubkeys(wt, workerFile, {
|
|
318
|
+
mode: "aggregatePubkeys",
|
|
319
|
+
pubkeys: chunk
|
|
320
|
+
});
|
|
321
|
+
})
|
|
322
|
+
);
|
|
323
|
+
if (partials.some((p) => p === null)) return false;
|
|
324
|
+
const aggregated = blsAggregatePublicKeys(partials);
|
|
325
|
+
return blsVerifyWithAggregatedPubkey(aggregated, message, signature);
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
function validateClusterLockInWorker(lock, safeRpcUrl) {
|
|
329
|
+
return __async(this, null, function* () {
|
|
330
|
+
var _a, _b;
|
|
331
|
+
const n = (_b = (_a = lock.distributed_validators) == null ? void 0 : _a.length) != null ? _b : 0;
|
|
332
|
+
if (n < MIN_VALIDATORS_FOR_VALIDATION_WORKER) return null;
|
|
333
|
+
const wt = loadWorkerThreads();
|
|
334
|
+
const workerFile = getWorkerPath("clusterLockValidationWorker.js");
|
|
335
|
+
if (wt === null || workerFile === null) return null;
|
|
336
|
+
return yield new Promise((resolve, reject) => {
|
|
337
|
+
let settled = false;
|
|
338
|
+
const worker = new wt.Worker(workerFile, {
|
|
339
|
+
workerData: { lock, safeRpcUrl }
|
|
340
|
+
});
|
|
341
|
+
let timer;
|
|
342
|
+
const finish = (result) => {
|
|
343
|
+
if (settled) return;
|
|
344
|
+
settled = true;
|
|
345
|
+
clearTimeout(timer);
|
|
346
|
+
worker.terminate();
|
|
347
|
+
resolve(result);
|
|
348
|
+
};
|
|
349
|
+
timer = setTimeout(() => {
|
|
350
|
+
if (settled) return;
|
|
351
|
+
settled = true;
|
|
352
|
+
clearTimeout(timer);
|
|
353
|
+
void worker.terminate();
|
|
354
|
+
reject(
|
|
355
|
+
new ClusterLockValidationTimeoutError(VALIDATION_WORKER_TIMEOUT_MS)
|
|
356
|
+
);
|
|
357
|
+
}, VALIDATION_WORKER_TIMEOUT_MS);
|
|
358
|
+
worker.once("message", (msg) => {
|
|
359
|
+
finish(msg === true);
|
|
360
|
+
});
|
|
361
|
+
worker.once("error", () => {
|
|
362
|
+
finish(null);
|
|
363
|
+
});
|
|
364
|
+
worker.once("exit", (code) => {
|
|
365
|
+
if (code !== 0) finish(null);
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
export {
|
|
372
|
+
ConflictError,
|
|
373
|
+
SignerRequiredError,
|
|
374
|
+
UnsupportedChainError,
|
|
375
|
+
ClusterLockValidationTimeoutError,
|
|
376
|
+
verifySharesBinding,
|
|
377
|
+
verifyBlsChecksParallel,
|
|
378
|
+
verifyBatchParallel,
|
|
379
|
+
verifyAggregateParallel,
|
|
380
|
+
validateClusterLockInWorker
|
|
381
|
+
};
|
|
382
|
+
//# sourceMappingURL=chunk-RYTIXFRX.js.map
|
|
@@ -0,0 +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":[]}
|