1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
// Off-main-thread nostr crypto. Schnorr signature checks (real secp256k1) on
// a relay backlog blocked the UI on boot / when a community opens — the
// carousel-stutter cause a CPU profile pinned to verifyEvent. This worker
// does that work on another thread; the main thread posts events and gets
// back a boolean per event, so the page never freezes on a burst.
//
// It also carries the other boot secp256k1 the profiler kept pinning:
// - 'openWraps': community (Concord) wraps — nip44 decrypt under the
// symmetric stream key + the seal's schnorr verify + hash checks. No
// secret crosses the thread boundary: the stream conversation key is
// group material every member derives.
// - 'unwrapDMs': NIP-17 gift wraps when the wallet holds the raw identity
// key (seed-derived / pasted nsec) — two ECDH ops + a seal verify each.
// Remote signers (bunker/extension) can't export their key, so their
// wraps never come here; the main thread keeps that path.
//
// Bundled to dist/verify-worker.js (see build.js). Same openWrap/unwrapDM/
// verifyEvent the main thread uses, so semantics are identical — this only
// moves WHERE they run.
import { verifyEvent } from 'nostr-tools/pure';
import { hexToBytes } from '@noble/hashes/utils';
import { openWrap } from './concord.js';
import { unwrapDM } from './dm.js';
self.onmessage = async (e) => {
const { reqId, events, op, wraps, convKey, sk } = e.data || {};
if (reqId == null) return;
if (Array.isArray(events)) {
const results = events.map((ev) => {
try { return verifyEvent(ev); } catch { return false; }
});
self.postMessage({ reqId, results });
return;
}
if (op === 'openWraps' && Array.isArray(wraps)) {
let stream = null;
try { stream = { convKey: hexToBytes(convKey) }; } catch {}
const results = wraps.map((w) => {
try { return stream ? openWrap(w, stream) : null; } catch { return null; }
});
self.postMessage({ reqId, results });
return;
}
if (op === 'unwrapDMs' && Array.isArray(wraps)) {
let key = null;
try { key = hexToBytes(sk); } catch {}
const results = [];
for (const w of wraps) {
results.push(key ? await unwrapDM(w, key).catch(() => null) : null);
}
self.postMessage({ reqId, results });
return;
}
self.postMessage({ reqId, results: null });
};
|