@prosopo/provider 5.11.1 → 5.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/.turbo/turbo-build$colon$cjs.log +116 -115
- package/.turbo/turbo-build$colon$tsc.log +27 -27
- package/.turbo/turbo-build.log +117 -116
- package/CHANGELOG.md +116 -0
- package/dist/api/blacklistRequestInspector.d.ts +5 -2
- package/dist/api/blacklistRequestInspector.d.ts.map +1 -1
- package/dist/api/blacklistRequestInspector.js +10 -5
- package/dist/api/blacklistRequestInspector.js.map +1 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js +1 -1
- package/dist/api/captcha/getPoWCaptchaChallenge.js +1 -1
- package/dist/api/captcha/getPuzzleCaptchaChallenge.js +1 -1
- package/dist/api/cpuProfiler.d.ts +32 -0
- package/dist/api/cpuProfiler.d.ts.map +1 -0
- package/dist/api/cpuProfiler.js +128 -0
- package/dist/api/cpuProfiler.js.map +1 -0
- package/dist/api/metrics.d.ts +9 -0
- package/dist/api/metrics.d.ts.map +1 -1
- package/dist/api/metrics.js +107 -1
- package/dist/api/metrics.js.map +1 -1
- package/dist/api/startProviderApi.d.ts.map +1 -1
- package/dist/api/startProviderApi.js +2 -0
- package/dist/api/startProviderApi.js.map +1 -1
- package/dist/cjs/api/blacklistRequestInspector.cjs +10 -5
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/handler.cjs +1 -1
- package/dist/cjs/api/captcha/getPoWCaptchaChallenge.cjs +1 -1
- package/dist/cjs/api/captcha/getPuzzleCaptchaChallenge.cjs +1 -1
- package/dist/cjs/api/cpuProfiler.cjs +129 -0
- package/dist/cjs/api/metrics.cjs +108 -0
- package/dist/cjs/api/startProviderApi.cjs +2 -0
- package/dist/cjs/tasks/captchaManager.cjs +61 -4
- package/dist/cjs/tasks/decisionMachine/decisionMachineRunner.cjs +2 -1
- package/dist/cjs/tasks/detection/decoderModules.cjs +14 -0
- package/dist/cjs/tasks/detection/decoderPool.cjs +183 -0
- package/dist/cjs/tasks/detection/getBotScore.cjs +9 -2
- package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs +6 -3
- package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasksUtils.cjs +5 -2
- package/dist/cjs/tasks/powCaptcha/powTasks.cjs +6 -3
- package/dist/cjs/tasks/powCaptcha/powTasksUtils.cjs +2 -2
- package/dist/cjs/tasks/puzzle/backgroundBuffer.cjs +3 -2
- package/dist/cjs/tasks/puzzle/nativeBackground.cjs +30 -0
- package/dist/cjs/tasks/puzzle/puzzleRenderer.cjs +3 -2
- package/dist/cjs/tasks/puzzleCaptcha/puzzleTasks.cjs +6 -3
- package/dist/tasks/captchaManager.d.ts +7 -0
- package/dist/tasks/captchaManager.d.ts.map +1 -1
- package/dist/tasks/captchaManager.js +61 -4
- package/dist/tasks/captchaManager.js.map +1 -1
- package/dist/tasks/decisionMachine/decisionMachineRunner.d.ts.map +1 -1
- package/dist/tasks/decisionMachine/decisionMachineRunner.js +2 -1
- package/dist/tasks/decisionMachine/decisionMachineRunner.js.map +1 -1
- package/dist/tasks/detection/decoderModules.d.ts +5 -0
- package/dist/tasks/detection/decoderModules.d.ts.map +1 -0
- package/dist/tasks/detection/decoderModules.js +13 -0
- package/dist/tasks/detection/decoderModules.js.map +1 -0
- package/dist/tasks/detection/decoderPool.d.ts +4 -0
- package/dist/tasks/detection/decoderPool.d.ts.map +1 -0
- package/dist/tasks/detection/decoderPool.js +183 -0
- package/dist/tasks/detection/decoderPool.js.map +1 -0
- package/dist/tasks/detection/getBotScore.js +9 -2
- package/dist/tasks/detection/getBotScore.js.map +1 -1
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.d.ts.map +1 -1
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.js +6 -3
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.js.map +1 -1
- package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.d.ts.map +1 -1
- package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.js +5 -2
- package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.js.map +1 -1
- package/dist/tasks/powCaptcha/powTasks.d.ts.map +1 -1
- package/dist/tasks/powCaptcha/powTasks.js +6 -3
- package/dist/tasks/powCaptcha/powTasks.js.map +1 -1
- package/dist/tasks/powCaptcha/powTasksUtils.js +1 -1
- package/dist/tasks/powCaptcha/powTasksUtils.js.map +1 -1
- package/dist/tasks/puzzle/backgroundBuffer.d.ts.map +1 -1
- package/dist/tasks/puzzle/backgroundBuffer.js +4 -3
- package/dist/tasks/puzzle/backgroundBuffer.js.map +1 -1
- package/dist/tasks/puzzle/nativeBackground.d.ts +3 -0
- package/dist/tasks/puzzle/nativeBackground.d.ts.map +1 -0
- package/dist/tasks/puzzle/nativeBackground.js +31 -0
- package/dist/tasks/puzzle/nativeBackground.js.map +1 -0
- package/dist/tasks/puzzle/puzzleRenderer.d.ts.map +1 -1
- package/dist/tasks/puzzle/puzzleRenderer.js +3 -2
- package/dist/tasks/puzzle/puzzleRenderer.js.map +1 -1
- package/dist/tasks/puzzleCaptcha/puzzleTasks.d.ts.map +1 -1
- package/dist/tasks/puzzleCaptcha/puzzleTasks.js +6 -3
- package/dist/tasks/puzzleCaptcha/puzzleTasks.js.map +1 -1
- package/dist/tests/integration/powCaptcha.integration.test.js +1 -1
- package/dist/tests/integration/powCaptcha.integration.test.js.map +1 -1
- package/dist/tests/integration/webBotAuthAuthenticated.integration.test.js +2 -2
- package/dist/tests/integration/webBotAuthAuthenticated.integration.test.js.map +1 -1
- package/dist/tests/unit/api/blacklistRequestInspector.unit.test.js +42 -1
- package/dist/tests/unit/api/blacklistRequestInspector.unit.test.js.map +1 -1
- package/dist/tests/unit/api/cpuProfiler.unit.test.d.ts +2 -0
- package/dist/tests/unit/api/cpuProfiler.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/api/cpuProfiler.unit.test.js +47 -0
- package/dist/tests/unit/api/cpuProfiler.unit.test.js.map +1 -0
- package/dist/tests/unit/api/cpuSpans.unit.test.d.ts +2 -0
- package/dist/tests/unit/api/cpuSpans.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/api/cpuSpans.unit.test.js +37 -0
- package/dist/tests/unit/api/cpuSpans.unit.test.js.map +1 -0
- package/dist/tests/unit/tasks/captchaManager.unit.test.js +83 -5
- package/dist/tests/unit/tasks/captchaManager.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/detection/decoderPool.unit.test.d.ts +2 -0
- package/dist/tests/unit/tasks/detection/decoderPool.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/tasks/detection/decoderPool.unit.test.js +59 -0
- package/dist/tests/unit/tasks/detection/decoderPool.unit.test.js.map +1 -0
- package/dist/tests/unit/tasks/detection/getBotScore.unit.test.js +39 -18
- package/dist/tests/unit/tasks/detection/getBotScore.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.unit.test.js +1 -1
- package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/puzzle/nativeBackground.unit.test.d.ts +2 -0
- package/dist/tests/unit/tasks/puzzle/nativeBackground.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/tasks/puzzle/nativeBackground.unit.test.js +50 -0
- package/dist/tests/unit/tasks/puzzle/nativeBackground.unit.test.js.map +1 -0
- package/package.json +25 -24
- package/dist/cjs/tasks/detection/decodeBehavior.cjs +0 -721
- package/dist/cjs/tasks/detection/decodePayload.cjs +0 -1325
- package/dist/cjs/tasks/detection/decodeSimd.cjs +0 -805
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
let node_inspector_promises = require("node:inspector/promises");
|
|
2
|
+
//#region src/api/cpuProfiler.ts
|
|
3
|
+
var enabled = () => process.env.PROSOPO_CPU_PROFILE_ENABLED === "true";
|
|
4
|
+
var numberFromEnv = (name, fallback) => {
|
|
5
|
+
const parsed = Number.parseInt(process.env[name] ?? "", 10);
|
|
6
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
7
|
+
};
|
|
8
|
+
var SYNTHETIC_FRAMES = /* @__PURE__ */ new Set([
|
|
9
|
+
"(idle)",
|
|
10
|
+
"(program)",
|
|
11
|
+
"(root)"
|
|
12
|
+
]);
|
|
13
|
+
/**
|
|
14
|
+
* Collapse a CDP profile into self time per call frame.
|
|
15
|
+
*
|
|
16
|
+
* `timeDeltas[i]` is the gap between sample `i-1` and sample `i`, so charging
|
|
17
|
+
* each delta to the node named by `samples[i]` gives self time — the time that
|
|
18
|
+
* frame was on top of the stack, not the time spent beneath it.
|
|
19
|
+
*/
|
|
20
|
+
var summariseProfile = (profile, topN) => {
|
|
21
|
+
const framesById = new Map(profile.nodes.map((node) => [node.id, node]));
|
|
22
|
+
const samples = profile.samples ?? [];
|
|
23
|
+
const timeDeltas = profile.timeDeltas ?? [];
|
|
24
|
+
const selfMicros = /* @__PURE__ */ new Map();
|
|
25
|
+
let accountedMicros = 0;
|
|
26
|
+
for (let i = 0; i < samples.length; i++) {
|
|
27
|
+
const id = samples[i];
|
|
28
|
+
const delta = timeDeltas[i];
|
|
29
|
+
if (id === void 0 || delta === void 0 || delta < 0) continue;
|
|
30
|
+
selfMicros.set(id, (selfMicros.get(id) ?? 0) + delta);
|
|
31
|
+
accountedMicros += delta;
|
|
32
|
+
}
|
|
33
|
+
let idleMicros = 0;
|
|
34
|
+
const ranked = [];
|
|
35
|
+
for (const [id, micros] of selfMicros) {
|
|
36
|
+
const node = framesById.get(id);
|
|
37
|
+
if (!node) continue;
|
|
38
|
+
const { functionName, url, lineNumber } = node.callFrame;
|
|
39
|
+
if (SYNTHETIC_FRAMES.has(functionName)) {
|
|
40
|
+
idleMicros += micros;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
ranked.push({
|
|
44
|
+
fn: functionName || "(anonymous)",
|
|
45
|
+
url,
|
|
46
|
+
line: lineNumber,
|
|
47
|
+
selfMs: micros / 1e3,
|
|
48
|
+
pct: 0
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
ranked.sort((a, b) => b.selfMs - a.selfMs);
|
|
52
|
+
const busyMs = Math.max(accountedMicros - idleMicros, 0) / 1e3;
|
|
53
|
+
for (const frame of ranked) frame.pct = busyMs > 0 ? frame.selfMs / busyMs * 100 : 0;
|
|
54
|
+
return {
|
|
55
|
+
durationMs: (profile.endTime - profile.startTime) / 1e3,
|
|
56
|
+
samples: samples.length,
|
|
57
|
+
accountedMs: accountedMicros / 1e3,
|
|
58
|
+
idlePct: accountedMicros > 0 ? idleMicros / accountedMicros * 100 : 0,
|
|
59
|
+
top: ranked.slice(0, topN)
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
var runOnce = async (logger) => {
|
|
63
|
+
const durationMs = numberFromEnv("PROSOPO_CPU_PROFILE_DURATION_MS", 5e3);
|
|
64
|
+
const samplingUs = numberFromEnv("PROSOPO_CPU_PROFILE_SAMPLING_US", 1e3);
|
|
65
|
+
const topN = numberFromEnv("PROSOPO_CPU_PROFILE_TOP_N", 25);
|
|
66
|
+
const session = new node_inspector_promises.Session();
|
|
67
|
+
try {
|
|
68
|
+
session.connect();
|
|
69
|
+
await session.post("Profiler.enable");
|
|
70
|
+
await session.post("Profiler.setSamplingInterval", { interval: samplingUs });
|
|
71
|
+
await session.post("Profiler.start");
|
|
72
|
+
await new Promise((resolve) => setTimeout(resolve, durationMs));
|
|
73
|
+
const { profile } = await session.post("Profiler.stop");
|
|
74
|
+
const summary = summariseProfile(profile, topN);
|
|
75
|
+
logger.info(() => ({
|
|
76
|
+
msg: "CPU profile",
|
|
77
|
+
data: {
|
|
78
|
+
windowMs: summary.durationMs,
|
|
79
|
+
samples: summary.samples,
|
|
80
|
+
idlePct: Number(summary.idlePct.toFixed(1)),
|
|
81
|
+
samplingUs,
|
|
82
|
+
top: summary.top.map((frame) => ({
|
|
83
|
+
fn: frame.fn,
|
|
84
|
+
at: `${frame.url}:${frame.line}`,
|
|
85
|
+
selfMs: Number(frame.selfMs.toFixed(2)),
|
|
86
|
+
pct: Number(frame.pct.toFixed(2))
|
|
87
|
+
}))
|
|
88
|
+
}
|
|
89
|
+
}));
|
|
90
|
+
} finally {
|
|
91
|
+
try {
|
|
92
|
+
session.disconnect();
|
|
93
|
+
} catch {}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Sample the isolate for a window, log the ranked self time, repeat.
|
|
98
|
+
*
|
|
99
|
+
* Returns a stop function. Never throws into the caller and never keeps the
|
|
100
|
+
* process alive: a broken profiler must not be able to take the provider with
|
|
101
|
+
* it, and a provider that is otherwise idle must still be able to exit.
|
|
102
|
+
*/
|
|
103
|
+
var startCpuProfiler = (logger) => {
|
|
104
|
+
if (!enabled()) return () => void 0;
|
|
105
|
+
const intervalMs = numberFromEnv("PROSOPO_CPU_PROFILE_INTERVAL_MS", 9e5);
|
|
106
|
+
let running = false;
|
|
107
|
+
const tick = () => {
|
|
108
|
+
if (running) return;
|
|
109
|
+
running = true;
|
|
110
|
+
runOnce(logger).catch((err) => {
|
|
111
|
+
logger.warn(() => ({
|
|
112
|
+
msg: "CPU profile failed",
|
|
113
|
+
err
|
|
114
|
+
}));
|
|
115
|
+
}).finally(() => {
|
|
116
|
+
running = false;
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
logger.info(() => ({
|
|
120
|
+
msg: "CPU profiler enabled",
|
|
121
|
+
data: { intervalMs }
|
|
122
|
+
}));
|
|
123
|
+
const timer = setInterval(tick, intervalMs);
|
|
124
|
+
timer.unref?.();
|
|
125
|
+
return () => clearInterval(timer);
|
|
126
|
+
};
|
|
127
|
+
//#endregion
|
|
128
|
+
exports.startCpuProfiler = startCpuProfiler;
|
|
129
|
+
exports.summariseProfile = summariseProfile;
|
package/dist/cjs/api/metrics.cjs
CHANGED
|
@@ -34,6 +34,21 @@ var SCORE_BUCKETS = [
|
|
|
34
34
|
5,
|
|
35
35
|
10
|
|
36
36
|
];
|
|
37
|
+
var SPAN_BUCKETS = [
|
|
38
|
+
1e-4,
|
|
39
|
+
25e-5,
|
|
40
|
+
5e-4,
|
|
41
|
+
.001,
|
|
42
|
+
.0025,
|
|
43
|
+
.005,
|
|
44
|
+
.01,
|
|
45
|
+
.025,
|
|
46
|
+
.05,
|
|
47
|
+
.1,
|
|
48
|
+
.25,
|
|
49
|
+
.5,
|
|
50
|
+
1
|
|
51
|
+
];
|
|
37
52
|
var KNOWN_ROUTES = /* @__PURE__ */ new Set([
|
|
38
53
|
...Object.values(_prosopo_types.ClientApiPaths),
|
|
39
54
|
...Object.values(_prosopo_types.PublicApiPaths),
|
|
@@ -160,6 +175,38 @@ var buildMetrics = () => {
|
|
|
160
175
|
help: "Healthz geo steering outcomes (steered/not_steered/target_down/geo_unavailable)",
|
|
161
176
|
labelNames: ["outcome"],
|
|
162
177
|
registers: [registry]
|
|
178
|
+
}),
|
|
179
|
+
decoderDuration: new prom_client.Histogram({
|
|
180
|
+
name: `${PREFIX}decoder_duration_seconds`,
|
|
181
|
+
help: "End-to-end duration of a detector payload decode, worker round trip included",
|
|
182
|
+
labelNames: ["decoder"],
|
|
183
|
+
buckets: SPAN_BUCKETS,
|
|
184
|
+
registers: [registry]
|
|
185
|
+
}),
|
|
186
|
+
decoderCallsTotal: new prom_client.Counter({
|
|
187
|
+
name: `${PREFIX}decoder_calls_total`,
|
|
188
|
+
help: "Detector payload decodes by decoder and outcome",
|
|
189
|
+
labelNames: ["decoder", "outcome"],
|
|
190
|
+
registers: [registry]
|
|
191
|
+
}),
|
|
192
|
+
syncSpanWallSecondsTotal: new prom_client.Counter({
|
|
193
|
+
name: `${PREFIX}sync_span_wall_seconds_total`,
|
|
194
|
+
help: "Seconds the event loop was held by named blocks of synchronous work",
|
|
195
|
+
labelNames: ["span"],
|
|
196
|
+
registers: [registry]
|
|
197
|
+
}),
|
|
198
|
+
syncSpanCallsTotal: new prom_client.Counter({
|
|
199
|
+
name: `${PREFIX}sync_span_calls_total`,
|
|
200
|
+
help: "Calls into each named block of synchronous work, successful or not",
|
|
201
|
+
labelNames: ["span"],
|
|
202
|
+
registers: [registry]
|
|
203
|
+
}),
|
|
204
|
+
syncSpanDuration: new prom_client.Histogram({
|
|
205
|
+
name: `${PREFIX}sync_span_duration_seconds`,
|
|
206
|
+
help: "Duration of each named block of synchronous work",
|
|
207
|
+
labelNames: ["span"],
|
|
208
|
+
buckets: SPAN_BUCKETS,
|
|
209
|
+
registers: [registry]
|
|
163
210
|
})
|
|
164
211
|
};
|
|
165
212
|
};
|
|
@@ -216,6 +263,65 @@ var setMaintenanceModeGauge = (on) => {
|
|
|
216
263
|
if (!metricsEnabled()) return;
|
|
217
264
|
getMetrics().maintenanceMode.set(on ? 1 : 0);
|
|
218
265
|
};
|
|
266
|
+
/**
|
|
267
|
+
* Time one detector decode end to end, worker round trip included.
|
|
268
|
+
*
|
|
269
|
+
* Wall time here is not event-loop time — the decode runs on a worker, so
|
|
270
|
+
* this is latency the request sees rather than delay it imposes on its
|
|
271
|
+
* neighbours. That is the point: watching it alongside
|
|
272
|
+
* `prosopo_nodejs_eventloop_lag_p99_seconds` is how you tell that the work
|
|
273
|
+
* moved rather than vanished.
|
|
274
|
+
*/
|
|
275
|
+
var recordDecoderOutcome = (decoder, outcome, durationSeconds) => {
|
|
276
|
+
if (!metricsEnabled()) return;
|
|
277
|
+
const m = getMetrics();
|
|
278
|
+
m.decoderCallsTotal.inc({
|
|
279
|
+
decoder,
|
|
280
|
+
outcome
|
|
281
|
+
});
|
|
282
|
+
m.decoderDuration.observe({ decoder }, durationSeconds);
|
|
283
|
+
};
|
|
284
|
+
/**
|
|
285
|
+
* Measure how long a named block of synchronous work holds the event loop.
|
|
286
|
+
*
|
|
287
|
+
* `increase(prosopo_sync_span_wall_seconds_total[1d])` by span is the ranking
|
|
288
|
+
* this exists to produce: which blocks are worth moving off the loop, ordered
|
|
289
|
+
* by how much of it they actually consume rather than by how expensive they
|
|
290
|
+
* look.
|
|
291
|
+
*
|
|
292
|
+
* Wall time, not CPU time, and that is not a compromise. Nothing else on this
|
|
293
|
+
* thread can run while `fn` does, so its wall time *is* the delay it imposes
|
|
294
|
+
* on every other request in flight — which is the quantity we care about.
|
|
295
|
+
*
|
|
296
|
+
* An earlier version also recorded a `process.cpuUsage()` delta and claimed it
|
|
297
|
+
* was exact for the same reason. It was not: cpuUsage covers the whole
|
|
298
|
+
* process, so V8's background GC and compiler threads and libuv's threadpool
|
|
299
|
+
* all landed on whichever span happened to be open. In production it read
|
|
300
|
+
* consistently *higher* than wall time, which is impossible for single-thread
|
|
301
|
+
* work and is what gave it away. Node exposes no per-thread CPU clock, so
|
|
302
|
+
* there is no fixed version of that metric to keep — process-wide CPU is
|
|
303
|
+
* already available as `prosopo_process_cpu_seconds_total`.
|
|
304
|
+
*
|
|
305
|
+
* This takes `() => T` and not an async callback for the same reason: wrapping
|
|
306
|
+
* an awaiting function would time every other request served during its awaits.
|
|
307
|
+
* Passing a function that *returns* a promise is fine and intended — several of
|
|
308
|
+
* our decoders are async in signature but do their work synchronously before
|
|
309
|
+
* resolving. Only the synchronous part is timed, so if one of them later grows
|
|
310
|
+
* a real await the span under-reports rather than over-reports.
|
|
311
|
+
*/
|
|
312
|
+
var measureSync = (span, fn) => {
|
|
313
|
+
if (!metricsEnabled()) return fn();
|
|
314
|
+
const wallBefore = process.hrtime.bigint();
|
|
315
|
+
try {
|
|
316
|
+
return fn();
|
|
317
|
+
} finally {
|
|
318
|
+
const wallSeconds = Number(process.hrtime.bigint() - wallBefore) / 1e9;
|
|
319
|
+
const m = getMetrics();
|
|
320
|
+
m.syncSpanCallsTotal.inc({ span });
|
|
321
|
+
m.syncSpanWallSecondsTotal.inc({ span }, wallSeconds);
|
|
322
|
+
m.syncSpanDuration.observe({ span }, wallSeconds);
|
|
323
|
+
}
|
|
324
|
+
};
|
|
219
325
|
var metricsMiddleware = () => {
|
|
220
326
|
return (req, res, next) => {
|
|
221
327
|
if (!metricsEnabled()) return next();
|
|
@@ -256,6 +362,7 @@ var metricsHandler = (env) => {
|
|
|
256
362
|
};
|
|
257
363
|
//#endregion
|
|
258
364
|
exports.getMetrics = getMetrics;
|
|
365
|
+
exports.measureSync = measureSync;
|
|
259
366
|
exports.metricsEnabled = metricsEnabled;
|
|
260
367
|
exports.metricsHandler = metricsHandler;
|
|
261
368
|
exports.metricsMiddleware = metricsMiddleware;
|
|
@@ -264,6 +371,7 @@ exports.recordBotScore = recordBotScore;
|
|
|
264
371
|
exports.recordCaptchaIssueError = recordCaptchaIssueError;
|
|
265
372
|
exports.recordCaptchaIssued = recordCaptchaIssued;
|
|
266
373
|
exports.recordCaptchaVerify = recordCaptchaVerify;
|
|
374
|
+
exports.recordDecoderOutcome = recordDecoderOutcome;
|
|
267
375
|
exports.recordDetectorTriggered = recordDetectorTriggered;
|
|
268
376
|
exports.recordDomainValidation = recordDomainValidation;
|
|
269
377
|
exports.recordFrictionlessDecision = recordFrictionlessDecision;
|
|
@@ -11,6 +11,7 @@ const require_handshakeTimingMiddleware = require("./handshakeTimingMiddleware.c
|
|
|
11
11
|
const require_public = require("./public.cjs");
|
|
12
12
|
const require_domainMiddleware = require("./domainMiddleware.cjs");
|
|
13
13
|
const require_createApiAdminRoutesProvider = require("./admin/createApiAdminRoutesProvider.cjs");
|
|
14
|
+
const require_cpuProfiler = require("./cpuProfiler.cjs");
|
|
14
15
|
const require_headerCheckMiddleware = require("./headerCheckMiddleware.cjs");
|
|
15
16
|
const require_ignoreMiddleware = require("./ignoreMiddleware.cjs");
|
|
16
17
|
const require_ipInfoMiddleware = require("./ipInfoMiddleware.cjs");
|
|
@@ -165,6 +166,7 @@ async function startProviderApi(env, admin = false, port) {
|
|
|
165
166
|
apiApp.use(express.default.json({ limit: "1mb" }));
|
|
166
167
|
apiApp.use(require_public.publicRouter(env));
|
|
167
168
|
apiApp.use(require_metrics.metricsMiddleware());
|
|
169
|
+
require_cpuProfiler.startCpuProfiler(env.logger);
|
|
168
170
|
if (!(process.env.NODE_ENV === "test" || env.config.defaultEnvironment === "development")) {
|
|
169
171
|
const rateLimits = {
|
|
170
172
|
...env.config.rateLimits,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const require_compositeIpAddress = require("../compositeIpAddress.cjs");
|
|
2
2
|
const require_blacklistRequestInspector = require("../api/blacklistRequestInspector.cjs");
|
|
3
3
|
const require_bundlePool = require("./detection/bundlePool.cjs");
|
|
4
|
+
const require_decoderPool = require("./detection/decoderPool.cjs");
|
|
4
5
|
const require_checkTrafficFilter = require("./spam/checkTrafficFilter.cjs");
|
|
5
6
|
const require_enrichDnsEvent = require("./dnsEvent/enrichDnsEvent.cjs");
|
|
6
7
|
const require_checkSpamEmail = require("./spam/checkSpamEmail.cjs");
|
|
@@ -115,6 +116,56 @@ var CaptchaManager = class CaptchaManager {
|
|
|
115
116
|
};
|
|
116
117
|
}
|
|
117
118
|
/**
|
|
119
|
+
* Decode the payloads a solution submission can carry, in one pass.
|
|
120
|
+
*
|
|
121
|
+
* Both decodes want the same session bundle and neither depends on the
|
|
122
|
+
* other, so the bundle is resolved once and the two decodes run together.
|
|
123
|
+
* Done separately they cost two bundle lookups and two serialised decoder
|
|
124
|
+
* round trips on the submit path, which measured as a ~22-25% latency
|
|
125
|
+
* regression on `pow/solution` and `puzzle/solution` once decoding moved
|
|
126
|
+
* to worker threads.
|
|
127
|
+
*
|
|
128
|
+
* Absent or undecodable payloads come back undefined rather than throwing,
|
|
129
|
+
* matching what the individual decoders already do: a submission is not
|
|
130
|
+
* worth failing over a signal we could not read.
|
|
131
|
+
*/
|
|
132
|
+
async decodeSubmissionPayloads(sessionId, payloads) {
|
|
133
|
+
const { behavioural, simd } = payloads;
|
|
134
|
+
if (!behavioural && !simd) return {};
|
|
135
|
+
let bundle;
|
|
136
|
+
try {
|
|
137
|
+
bundle = await this.resolveBundleBySessionId(sessionId);
|
|
138
|
+
} catch (err) {
|
|
139
|
+
this.logger?.warn(() => ({
|
|
140
|
+
msg: "Could not resolve the detector bundle for a submission",
|
|
141
|
+
data: { sessionId },
|
|
142
|
+
err
|
|
143
|
+
}));
|
|
144
|
+
return {};
|
|
145
|
+
}
|
|
146
|
+
const tolerate = async (decoding, which) => {
|
|
147
|
+
if (!decoding) return void 0;
|
|
148
|
+
try {
|
|
149
|
+
return await decoding ?? void 0;
|
|
150
|
+
} catch (err) {
|
|
151
|
+
this.logger?.warn(() => ({
|
|
152
|
+
msg: "Failed to decode a submission payload",
|
|
153
|
+
data: {
|
|
154
|
+
decoder: which,
|
|
155
|
+
sessionId
|
|
156
|
+
},
|
|
157
|
+
err
|
|
158
|
+
}));
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const [decodedBehavioural, decodedSimd] = await Promise.all([tolerate(behavioural ? this.decryptBehavioralData(behavioural, bundle) : void 0, "behaviour"), tolerate(simd ? this.decryptSimdReadingsForAttach(simd, bundle) : void 0, "simd")]);
|
|
163
|
+
return {
|
|
164
|
+
...decodedBehavioural && { behavioural: decodedBehavioural },
|
|
165
|
+
...decodedSimd && { simd: decodedSimd }
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
118
169
|
* Decrypt + first-hop-wins attach. Resolves the session's detector pool
|
|
119
170
|
* bundle (promoted onto the session record at frictionless time) to decrypt.
|
|
120
171
|
* No-op on decrypt failure / no bundle.
|
|
@@ -409,9 +460,12 @@ var CaptchaManager = class CaptchaManager {
|
|
|
409
460
|
this.logger?.warn(() => ({ msg: "No detector bundle resolved for SIMD readings — dropping field" }));
|
|
410
461
|
return null;
|
|
411
462
|
}
|
|
412
|
-
const decryptSimdReadings = (await Promise.resolve().then(() => require("./detection/decodeSimd.cjs"))).default;
|
|
413
463
|
try {
|
|
414
|
-
return await
|
|
464
|
+
return await require_decoderPool.decode("simd", [
|
|
465
|
+
encryptedData,
|
|
466
|
+
bundle.key,
|
|
467
|
+
bundle.innerConfig
|
|
468
|
+
]);
|
|
415
469
|
} catch (err) {
|
|
416
470
|
this.logger?.warn(() => ({
|
|
417
471
|
msg: "Failed to decrypt SIMD readings with the session's bundle",
|
|
@@ -425,9 +479,12 @@ var CaptchaManager = class CaptchaManager {
|
|
|
425
479
|
this.logger?.warn(() => ({ msg: "No detector bundle resolved for behavioral data — dropping field" }));
|
|
426
480
|
return null;
|
|
427
481
|
}
|
|
428
|
-
const decryptBehavioralData = (await Promise.resolve().then(() => require("./detection/decodeBehavior.cjs"))).default;
|
|
429
482
|
try {
|
|
430
|
-
const result = await
|
|
483
|
+
const result = await require_decoderPool.decode("behaviour", [
|
|
484
|
+
encryptedData,
|
|
485
|
+
bundle.key,
|
|
486
|
+
bundle.innerConfig
|
|
487
|
+
]);
|
|
431
488
|
this.logger?.info(() => ({
|
|
432
489
|
msg: "Behavioral data decrypted successfully",
|
|
433
490
|
data: {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_metrics = require("../../api/metrics.cjs");
|
|
2
3
|
let _prosopo_types = require("@prosopo/types");
|
|
3
4
|
let node_crypto = require("node:crypto");
|
|
4
5
|
let node_vm = require("node:vm");
|
|
@@ -250,7 +251,7 @@ var DecisionMachineRunner = class DecisionMachineRunner {
|
|
|
250
251
|
if (options.optional) return void 0;
|
|
251
252
|
throw new Error(`Decision machine must export one of: ${exportNames.join(", ")}`);
|
|
252
253
|
}
|
|
253
|
-
const result = await this.withTimeout(Promise.resolve(named.fn(options.input)), EXEC_TIMEOUT_MS);
|
|
254
|
+
const result = await this.withTimeout(Promise.resolve(require_metrics.measureSync("decision_machine_decide", () => named.fn(options.input))), EXEC_TIMEOUT_MS);
|
|
254
255
|
const parsed = schema.safeParse(result);
|
|
255
256
|
if (!parsed.success) throw new Error(`Decision machine '${named.name}' output failed validation`);
|
|
256
257
|
return parsed.data;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
var FILENAMES = {
|
|
2
|
+
payload: "decodePayload.js",
|
|
3
|
+
simd: "decodeSimd.js",
|
|
4
|
+
behaviour: "decodeBehavior.js"
|
|
5
|
+
};
|
|
6
|
+
var decoderModuleUrl = (decoder) => new URL(`./${FILENAMES[decoder]}`, require("url").pathToFileURL(__filename).href).href;
|
|
7
|
+
var decoderModuleUrls = () => ({
|
|
8
|
+
payload: decoderModuleUrl("payload"),
|
|
9
|
+
simd: decoderModuleUrl("simd"),
|
|
10
|
+
behaviour: decoderModuleUrl("behaviour")
|
|
11
|
+
});
|
|
12
|
+
//#endregion
|
|
13
|
+
exports.decoderModuleUrl = decoderModuleUrl;
|
|
14
|
+
exports.decoderModuleUrls = decoderModuleUrls;
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
const require_metrics = require("../../api/metrics.cjs");
|
|
2
|
+
const require_decoderModules = require("./decoderModules.cjs");
|
|
3
|
+
let _prosopo_logger = require("@prosopo/logger");
|
|
4
|
+
let node_os = require("node:os");
|
|
5
|
+
let node_worker_threads = require("node:worker_threads");
|
|
6
|
+
//#region src/tasks/detection/decoderPool.ts
|
|
7
|
+
var logger = (0, _prosopo_logger.getLogger)("info", "provider:decoder-pool");
|
|
8
|
+
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
9
|
+
var numberFromEnv = (name, fallback) => {
|
|
10
|
+
const raw = process.env[name];
|
|
11
|
+
if (raw === void 0 || raw === "") return fallback;
|
|
12
|
+
const parsed = Number.parseInt(raw, 10);
|
|
13
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Workers to run. Defaults to a small share of the box, since decoding is one
|
|
17
|
+
* of several things the node does and the rest of it still needs cores.
|
|
18
|
+
*
|
|
19
|
+
* `PROSOPO_DECODER_WORKERS=0` runs every decode inline on the main thread —
|
|
20
|
+
* the behaviour before this existed. That is the kill switch: it needs a
|
|
21
|
+
* restart but not a rollback.
|
|
22
|
+
*/
|
|
23
|
+
var poolSize = () => numberFromEnv("PROSOPO_DECODER_WORKERS", Math.max(1, Math.min(4, (0, node_os.availableParallelism)() - 1)));
|
|
24
|
+
var timeoutMs = () => numberFromEnv("PROSOPO_DECODER_TIMEOUT_MS", DEFAULT_TIMEOUT_MS);
|
|
25
|
+
var WORKER_SOURCE = `
|
|
26
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
27
|
+
|
|
28
|
+
const decoders = {};
|
|
29
|
+
for (const [name, url] of Object.entries(workerData.urls)) {
|
|
30
|
+
decoders[name] = (await import(url)).default;
|
|
31
|
+
}
|
|
32
|
+
parentPort.postMessage({ ready: true });
|
|
33
|
+
|
|
34
|
+
parentPort.on("message", async ({ id, decoder, args }) => {
|
|
35
|
+
try {
|
|
36
|
+
const result = await decoders[decoder](...args);
|
|
37
|
+
parentPort.postMessage({ id, ok: true, result });
|
|
38
|
+
} catch (err) {
|
|
39
|
+
parentPort.postMessage({
|
|
40
|
+
id,
|
|
41
|
+
ok: false,
|
|
42
|
+
message: err instanceof Error ? err.message : String(err),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
`;
|
|
47
|
+
var pool;
|
|
48
|
+
var nextCallId = 0;
|
|
49
|
+
var nextWorker = 0;
|
|
50
|
+
var inlineFallback = false;
|
|
51
|
+
var spawn = () => {
|
|
52
|
+
const worker = new node_worker_threads.Worker(WORKER_SOURCE, {
|
|
53
|
+
eval: true,
|
|
54
|
+
workerData: { urls: require_decoderModules.decoderModuleUrls() }
|
|
55
|
+
});
|
|
56
|
+
const pooled = {
|
|
57
|
+
worker,
|
|
58
|
+
inFlight: /* @__PURE__ */ new Map(),
|
|
59
|
+
ready: new Promise((resolve, reject) => {
|
|
60
|
+
worker.once("message", () => resolve());
|
|
61
|
+
worker.once("error", reject);
|
|
62
|
+
})
|
|
63
|
+
};
|
|
64
|
+
worker.on("message", (msg) => {
|
|
65
|
+
if (msg.id === void 0) return;
|
|
66
|
+
const pending = pooled.inFlight.get(msg.id);
|
|
67
|
+
if (!pending) return;
|
|
68
|
+
pooled.inFlight.delete(msg.id);
|
|
69
|
+
clearTimeout(pending.timer);
|
|
70
|
+
if (msg.ok) pending.resolve(msg.result);
|
|
71
|
+
else pending.reject(new Error(msg.message ?? "decoder failed"));
|
|
72
|
+
});
|
|
73
|
+
const abandon = (reason) => {
|
|
74
|
+
for (const [id, pending] of pooled.inFlight) {
|
|
75
|
+
clearTimeout(pending.timer);
|
|
76
|
+
pending.reject(new Error(reason));
|
|
77
|
+
pooled.inFlight.delete(id);
|
|
78
|
+
}
|
|
79
|
+
replace(pooled);
|
|
80
|
+
};
|
|
81
|
+
worker.on("error", (err) => {
|
|
82
|
+
logger.error(() => ({
|
|
83
|
+
msg: "Decoder worker errored",
|
|
84
|
+
err
|
|
85
|
+
}));
|
|
86
|
+
abandon(`decoder worker errored: ${err.message}`);
|
|
87
|
+
});
|
|
88
|
+
worker.on("exit", (code) => {
|
|
89
|
+
if (code === 0) return;
|
|
90
|
+
logger.error(() => ({
|
|
91
|
+
msg: "Decoder worker exited",
|
|
92
|
+
data: { code }
|
|
93
|
+
}));
|
|
94
|
+
abandon(`decoder worker exited with code ${code}`);
|
|
95
|
+
});
|
|
96
|
+
worker.unref();
|
|
97
|
+
return pooled;
|
|
98
|
+
};
|
|
99
|
+
var replace = (dead) => {
|
|
100
|
+
if (!pool) return;
|
|
101
|
+
const index = pool.indexOf(dead);
|
|
102
|
+
if (index === -1) return;
|
|
103
|
+
try {
|
|
104
|
+
pool[index] = spawn();
|
|
105
|
+
} catch (err) {
|
|
106
|
+
logger.error(() => ({
|
|
107
|
+
msg: "Could not replace decoder worker",
|
|
108
|
+
err
|
|
109
|
+
}));
|
|
110
|
+
pool.splice(index, 1);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
var getPool = () => {
|
|
114
|
+
if (inlineFallback) return void 0;
|
|
115
|
+
if (pool) return pool.length > 0 ? pool : void 0;
|
|
116
|
+
const size = poolSize();
|
|
117
|
+
if (size === 0) {
|
|
118
|
+
inlineFallback = true;
|
|
119
|
+
logger.info(() => ({ msg: "Decoder workers disabled, decoding inline" }));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
pool = Array.from({ length: size }, () => spawn());
|
|
124
|
+
logger.info(() => ({
|
|
125
|
+
msg: "Decoder pool started",
|
|
126
|
+
data: { size }
|
|
127
|
+
}));
|
|
128
|
+
return pool;
|
|
129
|
+
} catch (err) {
|
|
130
|
+
inlineFallback = true;
|
|
131
|
+
logger.error(() => ({
|
|
132
|
+
msg: "Could not start decoder pool, falling back to inline decoding",
|
|
133
|
+
err
|
|
134
|
+
}));
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
var runInline = async (decoder, args) => {
|
|
139
|
+
return (await import(require_decoderModules.decoderModuleUrl(decoder))).default(...args);
|
|
140
|
+
};
|
|
141
|
+
var runOnWorker = async (pooled, decoder, args) => {
|
|
142
|
+
await pooled.ready;
|
|
143
|
+
const id = nextCallId++;
|
|
144
|
+
return new Promise((resolve, reject) => {
|
|
145
|
+
const timer = setTimeout(() => {
|
|
146
|
+
pooled.inFlight.delete(id);
|
|
147
|
+
pooled.worker.terminate();
|
|
148
|
+
reject(/* @__PURE__ */ new Error(`decoder '${decoder}' timed out`));
|
|
149
|
+
}, timeoutMs());
|
|
150
|
+
timer.unref?.();
|
|
151
|
+
pooled.inFlight.set(id, {
|
|
152
|
+
resolve,
|
|
153
|
+
reject,
|
|
154
|
+
timer
|
|
155
|
+
});
|
|
156
|
+
pooled.worker.postMessage({
|
|
157
|
+
id,
|
|
158
|
+
decoder,
|
|
159
|
+
args
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Decode off the event loop, falling back to inline decoding if the pool is
|
|
165
|
+
* unavailable. Either way the contract is the one the callers already handle:
|
|
166
|
+
* resolves with the decoder's result, rejects with its error.
|
|
167
|
+
*/
|
|
168
|
+
var decode = async (decoder, args) => {
|
|
169
|
+
const started = process.hrtime.bigint();
|
|
170
|
+
const workers = getPool();
|
|
171
|
+
const pooled = workers?.[nextWorker++ % workers.length];
|
|
172
|
+
try {
|
|
173
|
+
const result = pooled ? await runOnWorker(pooled, decoder, args) : await runInline(decoder, args);
|
|
174
|
+
require_metrics.recordDecoderOutcome(decoder, "ok", elapsedSeconds(started));
|
|
175
|
+
return result;
|
|
176
|
+
} catch (err) {
|
|
177
|
+
require_metrics.recordDecoderOutcome(decoder, "error", elapsedSeconds(started));
|
|
178
|
+
throw err;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
var elapsedSeconds = (started) => Number(process.hrtime.bigint() - started) / 1e9;
|
|
182
|
+
//#endregion
|
|
183
|
+
exports.decode = decode;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const require_decoderPool = require("./decoderPool.cjs");
|
|
2
2
|
//#region src/tasks/detection/getBotScore.ts
|
|
3
3
|
var MAX_KEYS = 64;
|
|
4
4
|
var MAX_KEY_LENGTH = 64;
|
|
@@ -60,7 +60,14 @@ var sanitiseDetectorData = (value) => {
|
|
|
60
60
|
return sanitised;
|
|
61
61
|
};
|
|
62
62
|
var getBotScore = async (payload, headHash, privateKeyString, innerConfigEncoded, payloadLayoutEncoded, keyMapEncoded) => {
|
|
63
|
-
const result = await
|
|
63
|
+
const result = await require_decoderPool.decode("payload", [
|
|
64
|
+
payload,
|
|
65
|
+
headHash,
|
|
66
|
+
privateKeyString,
|
|
67
|
+
innerConfigEncoded,
|
|
68
|
+
payloadLayoutEncoded,
|
|
69
|
+
keyMapEncoded
|
|
70
|
+
]);
|
|
64
71
|
const baseBotScore = result.score;
|
|
65
72
|
if (baseBotScore === void 0 || Number.isNaN(baseBotScore)) return {
|
|
66
73
|
baseBotScore: 1,
|
|
@@ -109,8 +109,11 @@ var ImgCaptchaManager = class extends require_captchaManager.CaptchaManager {
|
|
|
109
109
|
};
|
|
110
110
|
const storedClientMetaData = require_clientMetaData.toStoredClientMetaData(clientMetaData);
|
|
111
111
|
const pendingRecord = await this.db.getPendingImageCommitment(requestHash);
|
|
112
|
-
const
|
|
113
|
-
|
|
112
|
+
const decodedPayloads = await this.decodeSubmissionPayloads(pendingRecord?.sessionId, {
|
|
113
|
+
behavioural: behavioralData,
|
|
114
|
+
simd: simdReadings
|
|
115
|
+
});
|
|
116
|
+
const decodedSimdReadings = decodedPayloads.simd;
|
|
114
117
|
const pushSimdAttachIfAny = (sessionId, writes) => {
|
|
115
118
|
if (decodedSimdReadings) writes.push(this.recordSessionSimdReadingsIfAbsentWithCache(sessionId, decodedSimdReadings, _prosopo_types.SimdReadingsStage.submit));
|
|
116
119
|
};
|
|
@@ -128,7 +131,7 @@ var ImgCaptchaManager = class extends require_captchaManager.CaptchaManager {
|
|
|
128
131
|
let behavioralDataPacked;
|
|
129
132
|
let deviceCapability;
|
|
130
133
|
if (behavioralData) try {
|
|
131
|
-
const decryptedData =
|
|
134
|
+
const decryptedData = decodedPayloads.behavioural;
|
|
132
135
|
if (decryptedData) {
|
|
133
136
|
this.logger?.info(() => ({
|
|
134
137
|
msg: "Behavioral analysis completed",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const require_metrics = require("../../api/metrics.cjs");
|
|
1
2
|
let _prosopo_common = require("@prosopo/common");
|
|
2
3
|
let _prosopo_datasets = require("@prosopo/datasets");
|
|
3
4
|
//#region src/tasks/imgCaptcha/imgCaptchaTasksUtils.ts
|
|
@@ -20,8 +21,10 @@ var buildTreeAndGetCommitmentId = (captchaSolutions) => {
|
|
|
20
21
|
failedFuncName: buildTreeAndGetCommitmentId.name,
|
|
21
22
|
commitmentId: null
|
|
22
23
|
} });
|
|
23
|
-
const
|
|
24
|
-
|
|
24
|
+
const layers = require_metrics.measureSync("merkle_build", () => {
|
|
25
|
+
const solutionsHashed = captchaSolutions.map((captcha) => nativeMerkle.computeCaptchaSolutionHash(captcha.captchaId, captcha.captchaContentId, captcha.solution, captcha.salt));
|
|
26
|
+
return nativeMerkle.buildMerkleLayers(solutionsHashed);
|
|
27
|
+
});
|
|
25
28
|
const tree = new _prosopo_datasets.CaptchaMerkleTree();
|
|
26
29
|
tree.hydrateFromLayers(layers);
|
|
27
30
|
const commitmentId = tree.root?.hash;
|
|
@@ -133,9 +133,12 @@ var PowCaptchaManager = class extends require_captchaManager.CaptchaManager {
|
|
|
133
133
|
}
|
|
134
134
|
let behavioralUpdates = {};
|
|
135
135
|
let decryptedBehavioralDataPacked;
|
|
136
|
+
const decodedPayloads = await this.decodeSubmissionPayloads(challengeRecord.sessionId, {
|
|
137
|
+
behavioural: behavioralData,
|
|
138
|
+
simd: simdReadings
|
|
139
|
+
});
|
|
136
140
|
if (behavioralData) try {
|
|
137
|
-
const
|
|
138
|
-
const decryptedData = await this.decryptBehavioralData(behavioralData, bundle);
|
|
141
|
+
const decryptedData = decodedPayloads.behavioural;
|
|
139
142
|
if (decryptedData) {
|
|
140
143
|
const dappAccount = (0, _prosopo_util.at)(challengeSplit, 2);
|
|
141
144
|
this.logger?.info(() => ({
|
|
@@ -182,7 +185,7 @@ var PowCaptchaManager = class extends require_captchaManager.CaptchaManager {
|
|
|
182
185
|
...(0, _prosopo_types.isBlockingCaptchaResult)(_prosopo_types.CaptchaType.pow, result) && { blocked: true },
|
|
183
186
|
...storedClientMetaData && { clientMetaData: storedClientMetaData }
|
|
184
187
|
}));
|
|
185
|
-
if (
|
|
188
|
+
if (decodedPayloads.simd) writePromises.push(this.recordSessionSimdReadingsIfAbsentWithCache(linkedSessionId, decodedPayloads.simd, _prosopo_types.SimdReadingsStage.submit));
|
|
186
189
|
}
|
|
187
190
|
await Promise.all(writePromises);
|
|
188
191
|
if (!correct) return { verified: correct };
|